What are Side Effects in React? Detailed Explanation and Real Examples

VnnTools

When starting your journey with React, you'll focus on building user interfaces (UI) from components, state, and props. Everything seems very "pure": components receive input (props, state) and return output (JSX). But then one day, you wonder: "How do I fetch data from a server? How do I change the page title? Or how do I set up a timer?".

What are Side Effects in React? Detailed Explanation and Real Examples

Welcome to Side Effects - a core concept that often confuses beginners. Don't worry! This article will decode everything about side effects in the most intuitive and easy-to-understand way.

πŸ€” What are Side Effects? The Simplest Way to Understand

Imagine your React component as a professional chef πŸ‘¨β€πŸ³. His main job is to receive ingredients (props, state) and cook a perfect dish (render the UI). This process is called a "pure function" - a closed, predictable process.

Side effects are all the actions that the chef must do outside of his kitchen.

Examples:

  • Going to the market to buy ingredients: Equivalent to calling an API to fetch data from a server.
  • Setting a timer for the oven: Equivalent to using setTimeout or setInterval.
  • Writing down recipes in a notebook: Equivalent to logging or saving data to localStorage.
  • Changing the "Dish of the Day" sign: Equivalent to directly manipulating the DOM (e.g., changing document.title).

In summary, side effects in React are any actions that your component performs to interact with the world outside of its normal render flow. That outside world could be an API, Local Storage, the browser's DOM, or any other system not directly controlled by React.

πŸ’₯ Why Do We Need to "Manage" Side Effects?

Why can't we just put a fetch() command directly inside the component body?

// ⛔️ DON'T DO THIS!
function MyComponent() {
  // Wrong! This fetch will run every time the component re-renders
  fetch('https://api.example.com/data')
    .then((res) => res.json())
    .then((data) => console.log(data))

  return <div>My Data</div>
}

The problem is that React can re-render a component multiple times for various reasons (state changes, props changes...). If you place side effects directly like above, they will run every time it renders. This leads to serious consequences:

  • Infinite Loop: Call API, then update state, updating state causes re-render, re-render calls API again... and so on in a loop.
  • Poor Performance: Sending hundreds of unnecessary network requests.
  • Unpredictable Behavior: You can't control when side effects will occur.

Therefore, React needs a "safe zone," a special place where we can execute and manage these side effects in a controlled manner. And that hero is...

πŸ¦Έβ€β™‚οΈ useEffect - The Ultimate Weapon for Handling Side Effects

useEffect is a Hook provided by React that allows you to perform side effects from within function components. It's like telling React: "Hey React, after you finish rendering the UI, please run this code for me!".

Basic Syntax

useEffect takes two arguments:

  1. A function containing your side effect code.
  2. A dependency array (optional) to control when the effect runs again.
import { useEffect } from 'react'

useEffect(() => {
  // Your side effect code will go here
  console.log('Component has been rendered or updated!')
}, [dependencies]) // <-- Dependency array

useEffect is a universal key, and its power lies in the dependency array.

Three Scenarios of the Dependency Array

  1. No dependency array: Effect runs after every render.

    useEffect(() => {
      // ⚠️ Be careful: Runs after every component re-render.
      // Very easy to cause infinite loops.
    })
    
  2. Empty array []: Effect only runs once, right after the first render.

    useEffect(() => {
      // Perfect for calling API once or initial setup.
      fetchData()
    }, []) // <-- Empty array
    
  3. Array with values [prop, state]: Effect will run first, and then only run again when one of the values in the array changes.

    const [userId, setUserId] = useState(1)
    
    useEffect(() => {
      // This effect will run again every time userId changes.
      fetchUser(userId)
    }, [userId]) // <-- Depends on userId
    

    This is the most powerful and commonly used scenario.

🧹 Cleanup Function

What happens if you set up a setInterval or a WebSocket connection? If the component is removed from the DOM tree (unmount), those connections still exist and cause memory leaks.

useEffect allows you to return a function from within it. This function is called a "cleanup function" and will be executed when:

  • The component is about to unmount.
  • Before the effect runs again on the next render.
useEffect(() => {
  const timerId = setInterval(() => {
    console.log('Tick!')
  }, 1000)

  // πŸ‘‡ This is the cleanup function
  return () => {
    console.log('Cleaning up timer...')
    clearInterval(timerId) // Cancel timer when component unmounts
  }
}, [])

🎯 Common useEffect Use Cases

Here are some real examples:

1. Fetching Data from API

function UserProfile({ userId }) {
  const [user, setUser] = useState(null)

  useEffect(() => {
    async function fetchUserData() {
      const response = await fetch(`https://api.example.com/users/${userId}`)
      const data = await response.json()
      setUser(data)
    }

    fetchUserData()
  }, [userId]) // Run again when userId changes

  return <div>{user ? user.name : 'Loading...'}</div>
}

2. DOM Manipulation

function DocumentTitleChanger({ title }) {
  useEffect(() => {
    // Side effect: change document title
    document.title = title
  }, [title]) // Update when title changes

  return <h1>Page Content</h1>
}

3. Event Listening

function WindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    function handleResize() {
      setWidth(window.innerWidth)
    }

    window.addEventListener('resize', handleResize)

    // Cleanup: remove event listener when component unmounts
    return () => {
      window.removeEventListener('resize', handleResize)
    }
  }, []) // Only need to set up once

  return <div>Window width: {width}px</div>
}

πŸ’‘ Beyond Side Effects: Custom Hooks & React Query

As your application grows, you'll find yourself repeating useEffect logic in many places. This is where Custom Hooks shine. You can encapsulate side effect logic into a reusable hook.

For example, creating a useFetch hook:

function useFetch(url) {
  const [data, setData] = useState(null)
  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((d) => setData(d))
  }, [url])
  return data
}

// Usage in component
function MyComponent() {
  const userData = useFetch('api/user')
  // ...
}

For complex side effects related to server data (caching, re-fetching, optimistic updates...), specialized libraries like TanStack Query (React Query) or SWR are excellent choices, helping you manage side effects powerfully with less code.

Summary: Key Points About Side Effects in React

  • Side Effects are any interactions with the "outside world" of your component, such as API calls, DOM manipulation, or timers.
  • We must manage side effects to avoid errors like infinite loops and unpredictable behavior.
  • useEffect is React's hook for handling side effects in a controlled manner.
  • Dependency arrays are the heart of useEffect, determining when the effect will run again.
  • Always remember to clean up side effects like subscriptions or timers to avoid memory leaks.
  • When logic becomes complex, consider creating Custom Hooks or using libraries like React Query.

Understanding and mastering useEffect is a turning point in becoming a proficient React developer. Hopefully, this article has helped you successfully "decode" this important concept!

Related Posts

What is Render in React? A Detailed Explanation for Beginners

Learn about React rendering mechanism from A to Z. This article clearly explains the concept and how React compares Virtual DOM to efficiently update the UI.

Everything About Redux in React: Definition, Principles and Detailed Examples

Explore the power of Redux in React. Learn about its working principles, core components and practical examples to help you master State Management.

State in ReactJS: Concepts, Usage, and Detailed Examples

Learn the concepts, declaration, and usage of State to manage dynamic data in your React application. See detailed guide with examples.

What is a React Component? A Detailed Guide for Beginners

Understanding React Components is the key to building modern web application interfaces. Read now to master common component types and how to create your first component.