Telerik blogs

See how to use the useEffect hook to run code when a state or prop value changes in our React app.

React comes with a few basic hooks to let us build components that work dynamically. Hooks are functions that return values and can be used in components to return different things when reactive values change.

In this article, we will look at how to use the useEffect hook.

The useEffect Hook

We use the useEffect hook to do things when any reactive values change. Reactive values include state and prop values.

We can use useEffect to run code when some state or prop value changes.

For instance, we write:

import { useEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(count);
  }, [count]);

  return (
    <div>
      <button onClick={() => setCount((c) => c + 1)}>increment</button>
    </div>
  );
}

to create the count state with the useState hook.

And then we call the useEffect hook to log the value of count when count changes. Then we add a button that increments the value of count by 1 when we click on it. The callback will be called whenever count changes since we put count in the array in the second argument of useEffect.

Whatever state and prop value we put in there will be watched by useEffect, and the callback in the first argument will be called whenever those values change.

In the callback, we call useState with a function that takes the previous count value as the argument and returns the new value of count derived from that.

By calling setCount with a function that returns the new state value based on the previous state, we can ensure that we always get the latest state value based on the previous value.

We can make the useEffect callback only run when the component mounts by calling the useEffect callback with an empty array.

And we can make the useEffect hook callback run whenever the component is rerendered by omitting the second argument when calling useEffect.

Running Cleanup Code When the Component Unmounts

We can run cleanup code in the useEffect when the component unmounts. To do this, we return a function that runs the cleanup code when the component unmounts.

For example, we write:

import { useEffect, useState } from "react";

export default function App() {
  const [screenSize, setScreenSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });
  const { width, height } = screenSize;

  const onResize = () => {
    setScreenSize({
      width: window.innerWidth,
      height: window.innerHeight
    });
  };
  useEffect(() => {
    window.addEventListener("resize", onResize);
    return () => {
      window.removeEventListener("resize", onResize);
    };
  }, []);

  return (
    <div>
      {width}x{height}
    </div>
  );
}

to call useEffect with a function that calls window.addEventListener to add the onResize function as the resize event listener function.

onResize will then be called when we resize the screen.

And to remove the event listener when the component unmounts, we call window.removeEventListener to remove the resize event listener when the App component unmounts.

The function that is returned in the useEffect callback is run when we unmount the component.

In onResize, we call setScreenSize to set screenSize to the screen’s width and height, so we see the width and height changes when we resize the screen.

Being able to call run code when the component mounts and unmounts with the useEffect makes it handy for synchronizing with external systems.

We can also manipulate external systems when a reactive value changes with the useEffect hook.

For example, we write:

import { useEffect, useRef, useState } from "react";

const ModalDialog = ({ open, children }) => {
  const ref = useRef();

  useEffect(() => {
    const dialog = ref.current;
    if (open) {
      dialog.showModal();
    }

    return () => {
      dialog.close();
    };
  }, [open]);

  return <dialog ref={ref}>{children}</dialog>;
};

export default function App() {
  const [open, setOpen] = useState(false);
  return (
    <div>
      <button onClick={() => setOpen(true)}>Open Dialog</button>
      <ModalDialog open={open}>
        <p>hello world</p>
        <button onClick={() => setOpen(false)}>Close Dialog</button>
      </ModalDialog>
    </div>
  );
}

to create the ModalDialog component.

It takes the open prop and calls useEffect that calls dialog.showModal to open the dialog box when the open state is true.

And when the component unmounts, we call dialog.close to close the dialog.

The ref is assigned as the dialog element’s ref prop value, so ref.current is the dialog element.

Create Custom Hooks with the useEffect Hook

The useEffect hook is one of the basic hooks that comes with React. Therefore, we can use it to create our own custom React hooks to encapsulate logic that we will reuse.

To do this, we just have to return the reactive values we want to return so they can be used in components. And the custom hook will just call other hooks to get the values we want to return.

For instance, we can clean up the previous example by moving the resize listener code into its own hook.

To do this, we write:

import { useEffect, useState } from "react";

const useScreenSize = () => {
  const [screenSize, setScreenSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });

  const onResize = () => {
    setScreenSize({
      width: window.innerWidth,
      height: window.innerHeight
    });
  };
  useEffect(() => {
    window.addEventListener("resize", onResize);
    return () => {
      window.removeEventListener("resize", onResize);
    };
  }, []);
  return screenSize;
};

export default function App() {
  const { width, height } = useScreenSize();

  return (
    <div>
      {width}x{height}
    </div>
  );
}

We create the useScreenSize hook that has the logic that we had previously in the App component. The useState and useEffect calls are moved into useScreenSize and they all do the same thing.

And we return screenSize so we can use the returned value in any component or another custom hook.

Then in App, we call useScreenSize to return the object with the width and height. And we render these values in the div.

Conclusion

The useEffect hook lets us run code that reacts to reactive property values. It is mainly used for running code that interacts with external systems with reactive values in the component changes.

We can make the useEffect callback run when reactive value changes or when the component mounts or run whenever the component rerenders.

Also, we can return a function in the useEffect to run any code that we want to run when the component unmounts. This is handy for running any cleanup code when a component unmounts.


About the Author

John Au-Yeung

John Au-Yeung is a frontend developer with 6+ years of experience. He is an avid blogger (visit his site at https://thewebdev.info/) and the author of Vue.js 3 By Example.

Related Posts

Comments

Comments are disabled in preview mode.