2

I am new to react and have been having hard time implementing this dependent dropdown. I want to get the lists of states and filter out location that renders to the second dropdown. Below is what i tried implementing. The States and Location data is pulled from an API.

I was able to pull the states data but got stuck at implementing the dependent dropdown. Any help will be appreciated.

import React, { useState, useEffect } from 'react'

function NullView() {

    // //API CALL
    const [error, setError] = useState(null);
    const [isLoaded, setIsLoaded] = useState(false);
    const [items, setItems] = useState([]);

    const [state, setState] = useState(null);
    const [locations, setLocations] = useState([])
    // console.log(count)
    let loadLocations = (e) => {
        e.preventDefault();

    }

    useEffect(() => {
        fetch("/api/merchant/all/states/")
            .then(res => res.json())
            .then((result) => {
                setIsLoaded(true);
                setItems(result);
            },
                (error) => {
                    setIsLoaded(true);
                    setError(error);
                }
            )
        if (state != null) {
            fetch("/api/merchant/locations/2/")
                .then(res => res.json())
                .then((result) => {
                    setIsLoaded(true);
                    setLocations(result);
                },
                    (error) => {
                        setIsLoaded(true);
                        setError(error);
                    }
                )
        }


    }, [])


    return (
        <div>
            <form onChange={loadLocations}>
                <label for="states">Choose a State:</label>
                <select id="states" name="states" onChange={(e) => setState(e.target.value)}>
                    <option value="">Select State</option>

                    {items.map(states => (

                        <option key={states.id} value={states.id}>{states.name}</option>
                    ))}

                </select>


                <label for="location">Choose a Location:</label>
                <select id="location" name="location" onChange={(e) => setLocation(e.target.value)}>
                    <option value="">Select Location</option>

                    {Location.map(location => (

                        <option key={location.id} value={location.id}>{location.name}</option>
                    ))}

                </select>

            </form>

        </div>
    )
}

export default NullView
4
  • 1
    So you want to filter Location based on a selection from the first select input? What is Location? Where do you attempt to filter the options for the second select input? What have you tried, and/or what isn't working as expected? Stackoverflow isn't a code writing service, please show us what you've tried already. Commented Apr 13, 2021 at 6:31
  • The above code is what i have tried, as earlier stated...I am completely new to react and have been on this for hours. I understand for newbies, experienced programmers like you may have thought I did not try anything. I have. Thanks for your help. The location get filtered with states.id Commented Apr 13, 2021 at 7:45
  • Sorry if I came across a little blunt, I'm not assuming you've not tried anything, I'm just asking to be pointed to where you are filtering data. Based on the comment below it seems you are wanting to use the result of the first fetch as part of a "query" in the second fetch request. Is this correct? Or is it that you want the two fetches to run separately? Just looking for a bit of clarification. If I guessed it seems the first fetch should run once on mount, and the second fetch should run when the state state updates so you can fetch by state.id. Commented Apr 13, 2021 at 7:50
  • Thanks a lot Drew...You are correct..the state.id is used to filter the locations. the api is suppose to be something like "/api/merchant/locations/"+states.id. I am sorry if i have not asked right or made some blunders. Commented Apr 13, 2021 at 8:10

2 Answers 2

2

You can break the two fetches into separate useEffect callbacks. The first useEffect callback fetches on component mount and updates the "state" options while the second useEffect has a dependency on the currently selected "state" value.

useEffect(() => {
  fetch("/api/merchant/all/states/")
    .then((res) => res.json())
    .then((result) => setItems(result))
    .catch((error) => setError(error))
    .finally(() => setIsLoaded(true));
}, []); // <-- fetch once when component mounts

useEffect(() => {
  if (state) {
    fetch(`/api/merchant/locations/${state}`) // <-- compute request URL
      .then((res) => res.json())
      .then((result) => setLocations(result))
      .catch((error) => setError(error))
      .finally(() => setIsLoaded(true));
  }
}, [state]); // <-- fetch when state updates

I believe you've also a typo in your render, Location should likely be locations state.

return (
  <div>
    <form onChange={loadLocations}>
      <label for="states">Choose a State:</label>
      <select
        id="states"
        name="states"
        onChange={(e) => setState(e.target.value)}
      >
        <option value="">Select State</option>
        {items.map((states) => (
          <option key={states.id} value={states.id}>
            {states.name}
          </option>
        ))}
      </select>

      <label for="location">Choose a Location:</label>
      <select
        id="location"
        name="location"
        onChange={(e) => setLocation(e.target.value)}
      >
        <option value="">Select Location</option>
        {locations.map((location) => ( // <-- render locations state
          <option key={location.id} value={location.id}>
            {location.name}
          </option>
        ))}
      </select>
    </form>
  </div>
);
Sign up to request clarification or add additional context in comments.

Comments

0

try this

import React, { useState, useEffect } from 'react'

function NullView() {

    // //API CALL
    const [error, setError] = useState(null);
    const [isLoaded, setIsLoaded] = useState(false);
    const [items, setItems] = useState([]);

    const [state, setState] = useState(null);
    const [locations, setLocations] = useState([])
    //i set api location in state
    const [apiLocation, setAPILocation] = useState(`/api/merchant/locations/`)
    // console.log(count)
    let loadLocations = (e) => {
        e.preventDefault();

    }

    useEffect(() => {
        fetch("/api/merchant/all/states/")
            .then(res => res.json())
            .then((result) => {
                setIsLoaded(true);
                setItems(result);
            },
                (error) => {
                    setIsLoaded(true);
                    setError(error);
                }
            )
        fetch(apiLocation)
            .then(res => res.json())
            .then((result) => {
                setIsLoaded(true);
                setLocations(result);
            },
                (error) => {
                    setIsLoaded(true);
                    setError(error);
                }
            )


    }, [])

    checkUrlLocation(url, newValue) {
      let splited = url.split('/')
      splited[splited.length - 1] = newValue
      return splited.join('/')

    }

    return (
        <div>
            <form onChange={loadLocations}>
                <label for="states">Choose a State:</label>
                <select id="states" name="states" onChange={(e) =>{
                    /* in this case, if you select i states, it will put state id in api location and re-run useEffect */
                    setAPILocation(checkUrlLocation(apiLocation,e.target.value))
                    setState(e.target.value)
                  }
                } >
                    <option value="">Select State</option>

                    {items.map(states => (

                        <option key={states.id} value={states.id}>{states.name}</option>
                    ))}

                </select>


                <label for="location">Choose a Location:</label>
                <select id="location" name="location" onChange={(e) => setLocation(e.target.value)}>
                    <option value="">Select Location</option>

                    {locations.map(location => (

                        <option key={location.id} value={location.id}>{location.name}</option>
                    ))}

                </select>

            </form>

        </div>
    )
}

export default NullView

3 Comments

Thanks for your help. I tried this and it did append the state.id to the apiLocation, but it did not send a request back to the server. also if for example i choose a state with an id of 1. apiLocation becomes "/api/merchant/locations/1" if i then choose another state with an id of 2, it becomes ""/api/merchant/locations/12". These are the two errors i noticed.
you just need no add validation , i already add validation in that code
Thanks a lot for your help...I learned some new stuffs today..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.