JordanLCo
JordanLCo

Reputation: 11

React Hooks, state not updating from fetch within useEffect

For my react-app/Express app I am trying to update the component state using the useEffects to run once when the component renders. Within useEffect I make a fetch to the express server.

const Favorites = ({ user }) => {
  const loggedIn = user.loginname === "" ? false : true;
  const [favs, setFavs] = useState([]);

  useEffect(() => {
    if (loggedIn) {
      fetch(`/user/favs/${user.loginname}`)
        .then((resp) => resp.json())
        .then((data) => {
          console.log(data);
          setFavs([...data]);
          console.log(favs);
        });
    }
  }, []);

  return (
    <div className="mt-d d-flex justify-content-center">
      {loggedIn ? (
        <FavoritesList favs={favs} />
      ) : (
        <h3 className="my-2">Please login to use this feature</h3>
      )}
    </div>
  );
};

I make a fetch call on line 11 and am able to print the results on line 14. I then try to update the component state using setFavs. My issue is that the state seems to not be updated or maybe there is some async issue.

const FavoritesList = ({ favs, prop }) => {
  const [data, setData] = useState([]);
  useEffect(() => {
    console.log(favs);
    // favs.forEach(item => console.log(item))
  }, []);

  return <h5>Dummy component</h5>;
};

When I try to print favs on line 16 or print favs within the child(FavoritesList) component it is being passed down to, I get an empty array.

Upvotes: 0

Views: 4127

Answers (3)

Closery
Closery

Reputation: 968

I think above two answers made everyhing clean, here is your lifecycle;

1 -> Favorites component initialized and rendered (state favs = [empty])
2 -> FavoritesList component initialized and rendered (prop favs = [empty])
3 -> FavoritesList useEffect called (console.log(favs) => [empty array])
4 -> Favorites useEffect called (state favs = [now has data])
5 -> FavoritesList component props is updated (prop favs = [has data]

(prop is updated but you didnt call console.log again, after update)

As you can see FavoritesList useEffect called before Favorites useEffect so it called when favs has no data. So if you add favs prop to array in useEffect in FavoritesList component, you will have one more step;

6 -> FavoritesList useEffect called again (console.log(favs) => [has data])

Because useEffect looks for that arrays elements and if one the elements changed it will triger itself again.

Here is your code with working example: codesandbox.io

Upvotes: 0

perellorodrigo
perellorodrigo

Reputation: 536

Change your useEffect on the FavoritesList to:

useEffect(() => {
console.log(favs)
}, [favs])

That way the useEffect will watch for any changes in the favs props. Using an empty array means that the useEffect will only trigger in the first render of the functional component. Meaning that it will not be triggered in any of the props change.

Upvotes: 0

Dmitry Minkovsky
Dmitry Minkovsky

Reputation: 38113

When I try to print favs on line 16 or print favs within the child(FavoritesList) component it is being passed down to, I get an empty array.

You're right, it is an async issue: your call to setFavs is async and favs is not yet set on line 16. Calling setFavs will cause your UI to re-render, eventually.

You won't see it in your other useEffect on line 35, either, because that useEffect hook also only runs on first render ([]), so that value is not there yet on first render. To see all updates to favs, try adding it to the dependencies array (like [favs]) or remove the dependencies array altogether.

Upvotes: 1

Related Questions