13

I am trying to understand the useEffect hook in-depth.

I would like to know when to use which method and why?

1.useEffect with no second paraments
 useEffect(()=>{})

2.useEffect with second paraments as []
  useEffect(()=>{},[])

3.useEffect with some arguments passed in the second parameter
 useEffect(()=>{},[arg])
John_ny
  • 577
  • 4
  • 28
  • 3
    1. Is being called on mount and every dependency update (any state / props change). 2. Only called on mount because you specified empty list of dependencies. 3. Called on mount and on change of any of the listed dependencies – ajobi Jan 21 '20 at 13:06
  • 2
    Dan Abramov has written an excellent blog post about useEffect: https://overreacted.io/a-complete-guide-to-useeffect/. You should read it ;-) – rphonika Jan 21 '20 at 13:16

2 Answers2

24
useEffect(callback);

// Example
useEffect(() => {
  console.log("executed after render phase");

  return () => {
    console.log("cleanup function after render");
  };
});
  • Runs on every component render.
  • Typically used for debugging, analogous to function's body execution on every render:
const Component = () => {
  callback()
  return <></>;
};

Note: There is still a difference, in execution time (see the next note). Check this sandbox logs.


useEffect(callback,[]);

// Example
useEffect(() => {
  const fetchUsers = async () => {
    const users = await fetch();
    setUsers(users);
  };

  fetchUsers();
  console.log("called on component's mount");

  return () => {
    console.log("called on component's unmount");
  };
}, []);
  • Usually used for initializing components state by data fetching etc.
  • Runs once on a component mount.
  • The cleanup function will run on component's unmount.

Gotchas:

Remember, there is a first render and then a mount.

Roughly saying, most of bugs regarding useEffect is not knowing how closures works and not paying attention to linting warnings.

Make sure the array includes all values from the component scope that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders - note in React docs.


useEffect(callback,[arg]);

// Example
useEffect(() => {
  console.log({ users });

  return () => {
    console.log("user value is changing");
  };
}, [users]);
  • Runs on change of arg value.
  • Usually used to run events on props/state change.
  • Multiple dependencies can be provided: [arg1,arg2,arg3...]
  • The cleanup function runs on arg value change.

Gotchas:

i.e compares the value of arg from the previous render and the current one, prevArg === arg ? doNothing() : callback().

  • Because in Javascript {} === {} || [] === [] is a falsy statement, if arg (users in our example) is an object, the callback will run on every render.

Additional Good to Know Points

const timeoutIdRef = useRef();

useEffect(() => {
  const timeoutId = timeoutIdRef.current;
  return () => {
  /*
    Using timeoutIdRef.current directly here is not safe
    since you can't guarantee the ref to exists in this point
    (especially when the component unmounts)
  */
    // Should get a lint warning here
    clearTimeout(timeoutIdRef.current); // BAD

    // Closure on timeoutId value
    clearTimeout(timeoutId); // GOOD
  };
}, [arg]);
const isMounted = useRef(false);

useEffect(() => {
  if (isMounted.current) {
    // first mount
  } else {
    isMounted.current = true;
  }
}, [arg]);

Keep reading:

Dennis Vash
  • 31,365
  • 5
  • 46
  • 76
  • 1
    Note that missing dependencies can lead to [stale closures](https://dmitripavlutin.com/react-hooks-stale-closures/). The following code: `const [s, setS] = useState(0); useEffect(() => setInterval(() => console.log(s), 1), []);` will only ever log 0 because the callback closes over s and even though the function is re created on every render it will only be called on mount and at time s was 0. See questions pop up many times having to do with this error and asynchronous setting of state is blamed but with functional components it's root cause is stale closures. – HMR Jun 28 '20 at 11:09
  • `run on every render` i would re-word that as `runs after each render` and the cleanup function `runs before each unmount, either the final unmount or before the next render attempt` – Derek May 06 '21 at 05:42
  • "runs before each unmount, either the final unmount or before the next render attempt" its sound like there is aways an "unmount attempt", but there isn't such. – Dennis Vash May 06 '21 at 05:51
1

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

1.useEffect with no second paraments : This is used when we want something to happen either when the component just mounted, or if it has been updated. Conceptually, we want it to happen after every render.

2.useEffect with second paraments as [] : This is used when we want something to happen at the time of mounting of the component, if only executes once at the time of mounting.It is closer to the familiar componentDidMount and componentWillUnmount.

3.useEffect with some arguments passed in the second parameter:This is used when we want something to happen at the time when the pramter passed eg. args have changed in your case.

For more info. check here: https://reactjs.org/docs/hooks-effect.html

neelesh bisht
  • 410
  • 3
  • 8