Skip to main content

Effects Intro

Effects

There are two types of logics in react components,

  1. Rendering code lives at the top level of your component. This is where you take the props and state, transform them, and return the JSX you want to see on the screen.

  2. Event handlers Event handlers contain “side effects” caused by a specific user action (for example, a button click or typing). It may submit an HTTP POST request to buy a product, or navigate the user to another screen.

Sometimes this isn’t enough. Consider a ChatRoom component that must connect to the chat server whenever it’s visible on the screen. Effects let you specify side effects that are caused by rendering itself, rather than by a particular event. Effects run at the end of a commit after the screen updates.

Don’t rush to add Effects to your components.

  • Keep in mind that Effects are typically used to “step out” of your React code and synchronize with some external system. This includes browser APIs, third-party widgets, network, and so on.
  • If your Effect only adjusts some state based on other state, you might not need an Effect.

How to write an Effect

Step 1: Declare an Effect

  • To declare an Effect in your component, import the useEffect Hook from React.
  • Then, call it at the top level of your component and put some code inside your Effect
  • Every time your component renders, React will update the screen and then run the code inside useEffect.
import { useEffect } from "react";

function MyComponent() {
useEffect(() => {
// Code here will run after *every* render
});
return <div />;
}

use case

function VideoPlayer({ src, isPlaying }) {
const ref = useRef(null);

if (isPlaying) {
ref.current.play(); // Calling these while rendering isn't allowed.
} else {
ref.current.pause(); // Also, this crashes.
}

return <video ref={ref} src={src} loop playsInline />;
}
danger

By default, Effects run after every render. This is why code like this will produce an infinite loop:

const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
});

Effects should usually synchronize your components with an external system. If there’s no external system and you only want to adjust some state based on other state, you might not need an Effect.


Step 2: Specify the Effect dependencies

  • By default, Effects run after every render. Often, this is not what you want.
  • We can skip unnecessarily re-running the Effect by specifying an array of dependencies as the second argument to the useEffect call.
  • React will only skip re-running the Effect if all of the dependencies you specify have exactly the same values as they had during the previous render. React compares the dependency values using the Object.is comparison.
useEffect(() => {
// This runs after every render
});

useEffect(() => {
// This runs only on mount (when the component appears)
}, []);

useEffect(() => {
// This runs on mount *and also* if either a or b have changed since the last render
}, [a, b]);

Step 3: Add cleanup if needed

  • Some Effects need to specify how to stop, undo, or clean up whatever they were doing. For example, “connect” needs “disconnect”, “subscribe” needs “unsubscribe”, and “fetch” needs either “cancel” or “ignore”. We can return a cleanup function inside Effects to do this.

  • React will call your cleanup function each time before the Effect runs again, and one final time when the component unmounts (gets removed).

useEffect(() => {
const connection = createConnection();
connection.connect();
return () => {
connection.disconnect();
};
}, []);

Lifecycle of Reactive Effects

  • Effects have a different lifecycle from components. Components may mount, update, or unmount. An Effect can only do two things: to start synchronizing something, and later to stop synchronizing it.

  • This cycle can happen multiple times if your Effect depends on props and state that change over time.

  • React provides a linter rule to check that you’ve specified your Effect’s dependencies correctly. This keeps your Effect synchronized to the latest props and state.

function ChatRoom({ roomId }) {
useEffect(() => {
// Effect’s body specifies how to start synchronizing
const connection = createConnection(serverUrl, roomId); // This Effect reads roomId
connection.connect();

// The cleanup function returned by your Effect specifies how to stop synchronizing
return () => {
connection.disconnect();
};
}, [roomId]); // Tell React that this Effect "depends on" roomId. Effect will resynchronize based on this
}