DEV Community

loading...
Cover image for The useEffect hook tribulations + Cheat Sheet

The useEffect hook tribulations + Cheat Sheet

boostup profile image Fred B. Updated on ・2 min read

Yesterday, while working on this project, I came to realise that logic contained in useFetchCollections was flawed in that it was invoked endlessly.

This had for perverse effect that the free daily allowed quota in firestore was reached !

Read access to the cloud database was forbidden for almost 24 hours !!!!

Therefore, to fix this, I pushed a commit that :

1) fixes the endless loop, and
2) now makes use of collections data from a local json file instead of fetching it from the database (using process.env.NODE_ENV the code determines whether the environment is production or development in order to conditionally fetch this data set in one data source or the other)

The useEffect hook tribulations

This loop happened because the data fetching was inside a useEffect hook, as seen in this file history:

  const fetchCollections = fetchCollectionsAsync(setCollectionsState);

  useEffect(() => {
    fetchCollections();
  }, [fetchCollections]);
  return collectionsState;
};

The problem was to have included fetchCollections as a dependency inside the array (the second argument that useEffect takes). Without its inclusion there, the React lib issued a warning :

React Hook useEffect has a missing dependency: `fetchCollections`.
Either include it or remove the dependency array. 
If `fetchCollections` changes too often, find the parent component that defines it and wrap that definition in useCallback

In this particular issue, I needed to ensure that useEffect fired only once, so the fix I issued is as follows:

  useEffect(() => {
    fetchCollectionsAsync(setCollectionsState);
  }, []);

The fix allowed to:

A) get rid of the React warning, and
B) get useEffect to behave as needed by providing an empty dependency array, to have it fire only once in the whole component lifecycle.

When NOT to pass array of dependency to useEffect

One of the suggestions found in the warning message was to remove the dependency array.

After some research, it turns out that we must omit this array of dependency when we want useEffect to trigger:

  • everytime the component it is in has finished rendering
  • whenever useEffect is invoked

Reminder about component rendering

A component will re-render when either:

  • props change
  • state change
  • the parent component re-renders

useEffect Cheat Sheet

Even though Dan Abramov, the lead dev in the React team will not like it, here is a cheat sheet which "maps" useEffect use cases in terms of Class-based component lifecycles.

ComponentDidMount

//Class
componentDidMount() {
    console.log('I just mounted!');
}

//Hooks
useEffect(() => {
    console.log('I just mounted!');
}, [])

ComponentWillUnmount

//Class
componentWillUnmount() {
    console.log('I am unmounting');
}

//Hooks
useEffect(() => {
    return () => console.log('I am unmounting');
}, [])

ComponentWillReceiveProps


//Class
componentWillReceiveProps(nextProps) {
    if (nextProps.count !== this.props.count) {
        console.log('count changed', nextProps.count);
    }
}

//Hooks
useEffect(() => {
    console.log('count changed', props.count);
}, [props.count])

Discussion

pic
Editor guide