React Hook - multiple "useEffect" fires at componentDidMount

Viewed 2516

I'm using multiple useEffect hooks to perform the componentDidMount and componentDidUpdate functionalities, however, looks like when component loads, all my useEffect fires initially...

const testComp = () => {
   const stateUserId = useSelector(state => { return state.userId; });
   const [userId, setUserId] = useState(stateUserId);
   const [active, setActive] = useState(false);
   const [accountId, setAccountId] = useState();
   
   useEffect(() => {
      console.log('component did mount...');
   }, [userId]);
   
   useEffect(() => {
      console.log('should trigger when userId changes...');
   }, [userId]);

   useEffect(() => {
      console.log('should trigger when active changes...');
   }, [active]);
   
   useEffect(() => {
      console.log('should trigger when accountId changes...');
   }, [accounted]);
  
   return (<div>...</div);
}

when my component mounts, I see all those console log there

component did mount...
should trigger when userId changes...
should trigger when active changes...
should trigger when accountId changes...

How could I only let my first useEffect fires, but the other three fires when the dependency changes only?

3 Answers

useEffect is not a direct replacement of componentDidMount and componentDidUpdate. Effect will run after each render, which is why you are seeing all those console logs. According to the React documentation, the second parameter of useEffect means

you can choose to fire them (effects) only when certain values have changed.

After the initial render, if the component is rendered again, only effects watch the corresponding value changes are triggered.

One way to achieve what you want is by creating additional variables to host initial values and do comparisons in the useEffect when you need to.

const testComp = () => {
  const initUserId =  useSelector(state => { return state.userId; });
  const stateUserId = initUserId;
   const [userId, setUserId] = useState(stateUserId);
   
   useEffect(() => {
      console.log('component did mount...');
   }, [userId]);
   
   useEffect(() => {
      if (userId !== initUserId) {
        console.log('should trigger when userId changes...');
      }
      
   }, [userId]);
  
   return <div>...</div>
}

Any useEffect hook will always be fired at component mount and when some of the variables in its dependency array changes. If you want to perform an action just when the variable changes you must check the incoming value first and do the required validations. Also your first useEffect must have an empty dependency array to fire it just when the component mounts, because as it is it will also be called when userId changes.

you can use custom useEffect:

const useCustomEffect = (func, deps) => {
    const didMount = useRef(false);

    useEffect(() => {
        if (didMount.current) func();
        else didMount.current = true;
    }, deps);
}

and use it in your component:


useCustomEffect(() => {
      console.log('trigger when userId changes...');
}, [userId]);

Related