How To Deal With Side Effects

Introduction

Certain components in React need to interact with things outside themselves:

  • querying data from a server
  • changing the component position on the webpage
  • sending data to a server when necessary

This kind of interaction with the outside world is called a side-effect.
theodinproject.com

Effects in React

Effects let you run some code to synchronize your component as necessary

  • on render or on reactive/state value change
  • rather than just in response to events.

You already know rendering code and event handlers; effects are the escape hatch for more complex interaction.

Using the useEffect hook

import { useState } from "react";

export default function Clock() {
  const [counter, setCounter] = useState(0);

  setInterval(() => {
    setCounter(count => count + 1)
  }, 1000);

  return (
    <p>{counter} seconds have passed.</p>
  );
}

What’s wrong?

  • Interval is set on every render → many overlapping intervals → issues!

Wrap in useEffect

import { useEffect, useState } from "react";

export default function Clock() {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    setInterval(() => {
      setCounter(count => count + 1);
    }, 1000);
  });

  return (
    <p>{counter} seconds have passed.</p>
  );
}

Better.. but still not ideal. Effect still runs every render unless you control it.

Dependency Array

The second argument to useEffect is the dependency array.

  • [] → run only on mount
  • [a, b] → run when a or b change
  • no array → run on every render

Example:

useEffect(() => {
  // runs only once
}, []);

Managing Dependencies & Clean-ups

Effects often need cleanup, such as clearing intervals or unsubscribing.
Also, let your linter help you manage dependencies instead of suppressing it.

Why is cleanup necessary?

Without cleanup, things like intervals or subscriptions persist after the component unmounts or before the effect re-runs >> leading to memory leaks or unexpected behaviour.

Example:

useEffect(() => {
  const interval = setInterval(() => { … }, 1000);

  return () => clearInterval(interval);
}, []);

Summary : Best Practices for useEffect

  1. Use useEffect to run side-effects after render
  2. Provide correct dependencies so effects don’t run unnecessarily
  3. Provide cleanup logic to avoid leaks
  4. Let the linter guide you - don’t suppress it
  5. Read about When not to use effects to avoid overusing them

Most Common Use Cases for Effects

  • Data fetching
  • Subscriptions (e.g., WebSocket, EventSource)