Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we need to return a function in the React Hook?

I'm working with Material-UI components on the project and is using AutoComplete component in my app.

In the example from Material-UI team, I came across an interesting example of the AutoComplete Ajax data: https://material-ui.com/components/autocomplete/#asynchronous-requests

They are using React Hooks to fetch the data from the server:

React.useEffect(() => {
    let active = true;

    if (!loading) {
      return undefined;
    }

    (async () => {
      const response = await fetch('https://country.register.gov.uk/records.json?page-size=5000');
      await sleep(1e3); // For demo purposes.
      const countries = await response.json();

      if (active) {
        setOptions(Object.keys(countries).map((key) => countries[key].item[0]));
      }
    })();

    return () => {
      active = false;
    };
  }, [loading]);

Why do we use active variable here? Why we return a function that changes this variable to false? It is always true in the if-statement. Thanks in advance for the answer

like image 415
Karen Avatar asked May 24 '26 02:05

Karen


2 Answers

The function returned from useEffect is a cleanup function. It is called when the component un-mounts - and is usually used to unsubscribe to events, cancel pending promises etc that were used in the useEffect.

The active variable is used make sure that you aren't updating the state on something that doesn't exist anymore. It's somewhat like the isMounted anti-pattern that existed in class components.

When you try to update state on an un-mounted component, React will throw a warning -

Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application.

Having the active variable prevents that in the following way:

  • Your component loads
  • The useEffect calls an async fetch - this will take time
  • Now say, before the server from the response is returned, you navigate away from the page (or perform some other action that un-mounts the component)
  • That will cause the component to unmount and the cleanup function to be called:
    return () => {
      active = false;
    };
  • active is now set to false
  • Finally, we get our response from the server. And now, it'll encounter the false active value, and not update the state.
      // active === false, 
      // this will skip `setOptions`
      if (active) {
        setOptions(...);
      }
like image 156
iamaatoh Avatar answered May 25 '26 16:05

iamaatoh


This is a pattern which is used to avoid two situations:

  1. updating the state if component containing this hook has already unmounted before HTTP request could complete.
  2. avoid race conditions

Function returned by callback function of useEffect hook is used to perform the clean up, like componentWillUnmount in class based components. This cleanup function runs when the component unmounts or before running the effect next time.

So if component is unmounted when the HTTP request was in progress, cleanup function of useEffect hook will run and will set active to false. After that, whenever the result of HTTP request returns, state won't be updated because active will be false.

See Effects with cleanup section of react docs to understand the cleanup function of useEffect hook and see Race Conditions to better understand the problem related to race conditions that is solved here by using active variable.

like image 20
Yousaf Avatar answered May 25 '26 16:05

Yousaf



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!