0

I'm sure this is just my lack of React understanding, but I'm trying to get a Component to update after the state is updated (which is happening asynchronously).

const [appState, setAppState] = useState({
        countries: null,
        languages: null
    })
useEffect(() => {
        const languageURL = `//localhost:8080/languages`,
              countryURL = '//localhost:8080/countries';
        axios.get(languageURL, {
            headers: {'Access-Control-Allow-Origin': '*'}
        })
            .then((langResponse) => {
                    const languages = langResponse.data.map(l => ({
                        value: l.id,
                        label: l.language + " (" + l.code + ")"
                    }))
                    setAppState({languages: languages});
                    console.log("updating languages");
                    return languages;
                }, (error) => {
                    console.log(error)
                }
            );
        axios.get(countryURL, {
            headers: {'Access-Control-Allow-Origin': '*'}
        })
            .then((countryResponse) => {
                    const countries = countryResponse.data.map(l => ({
                        value: l.id,
                        label: l.name + " (" + l.code + ")"
                    }))
                    setAppState({countries: countries});
                    console.log("updating countries");
                    return countries;
                }, (error) => {
                    console.log(error)
                }
            );
    }, [setAppState]);

My Component looks like this (I have one for each object set):

<Select
  options={appState.countries}
  isSearchable
/>

I've verified that the data is coming down and that it matches the format. The problem appears to be that each Component is not rendering when the supplied state object updates - only one is picking up changes. It also seems like it's whichever resolves first - the other one is empty. It's almost the opposite of this question - they are updating immediately, before the other set has been resolved.

end-user
  • 2,845
  • 6
  • 30
  • 56
  • 2
    `Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:` https://reactjs.org/docs/hooks-reference.html#usestate – Aprillion Sep 04 '20 at 16:17
  • Well, if you replace the state with `{languages: languages}` then it won't have the `countries` entry anymore and vice versa. Usually you'd have a separate `useState` for them instead of one that contains both. – Guy Incognito Sep 04 '20 at 16:18
  • 1
    Does this answer your question? [Updating and merging state object using React useState() hook](https://stackoverflow.com/questions/55342406/updating-and-merging-state-object-using-react-usestate-hook) – Garrett Motzner Sep 04 '20 at 16:26
  • `useState` works a bit differently than regular react state. To a degree, using a single object for multiple state values is an anti pattern. If you have separate life cycles for different state updates (the two values are independent of each other, as they appear here), it is easier to use 2 different `useState` calls. In your case, one for languages and one for countries. The exception to this would be if you need to pass around the whole "appState" object. In that case, also consider the reducer pattern. – Garrett Motzner Sep 04 '20 at 16:30
  • 1
    See also: https://github.com/arielweinberger/react-hooks-accessor-state for another way to use state. – Garrett Motzner Sep 04 '20 at 16:45

2 Answers2

1

i think you should separate the state if you don't want to set it together i tested it and I got error when set it separately , I think it's better separate it and write useEffect for each state if it's require

tmohammad78
  • 177
  • 2
  • 17
1

Ok, yeah, it looks like the answer is to create separate state objects and update each accordingly; effectively what Garrett Motzner and tmohammad78 were saying. I think this lets the Component that are depending on them see the state change discretely. Would look like this:

const [countries, setCountries] = useState();
const [languages, setLanguages] = useState();

useEffect(() => {
    ...
    setLanguages(languages);
    ...
    setCountries(countries);
    ...
 }, [setLanguages, setCountries]);

If this were a larger Component, perhaps react-hooks-accessor-state would be a good approach.

end-user
  • 2,845
  • 6
  • 30
  • 56