Skip to main content

The useEffect Hook

React components must be pure. They should only calculate their output and do nothing else. However, sometimes you need to step outside the React lifecycle to perform actions like fetching data from a network API, manually changing the DOM layout, or setting up a socket subscription.

These operations are called Side Effects.

Basic Syntax

The useEffect hook tells React to run a specific function after the component renders to the screen.

import { useEffect, useState } from 'react';

export default function ChatRoom() {
const [roomId, setRoomId] = useState('general');

useEffect(() => {
console.log(`Connecting to the ${roomId} room at render`);
});

return <div>Room: {roomId}</div>;
}

The Dependency Array

By default, an effect runs after every single render. This is usually not what you want, as fetching an API on every render causes an infinite loop in combination with useState.

You can control when the effect runs by passing an array as the second argument, called the dependency array.

// 1. Runs on EVERY render
useEffect(() => { ... });

// 2. Runs ONLY ONE TIME when the component mounts
useEffect(() => { ... }, []);

// 3. Runs when the component mounts, AND whenever specific values change
useEffect(() => { ... }, [roomId, serverUrl]);

The Cleanup Function

Some effects require cleanup to reduce memory leaks. For example, if you set up a WebSocket connection, you must close it when the component is unmounted from the screen.

You handle this by returning a function from inside your effect. React will run this cleanup function before the component unmounts, or right before running the effect again.

useEffect(() => {
const connection = createConnection();
connection.connect();

// Cleanup function
return () => {
connection.disconnect();
};
}, [roomId]);