Understanding useEffect and React.memo in React

What is useEffect?

  • useEffect is a React hook used for handling side effects (code that interacts with the outside world or modifies things outside React’s rendering, e.g., API calls, timers, DOM updates).

  • It runs after React has finished rendering the component.


Why does it run at the end?

Here’s an implementation of useEffect:

function App() {
    let[color, setColor] = useState('black');
    console.log('render');

    useEffect(()=>{
        console.log('Render useEffect');
        document.body.style.backgroundColor = color;
    }, [color]);

    console.log('Last Line Executed');

    return (
        <div>
            <button onClick={()=>{setColor('red')}}>Red</button>
            <button onClick={()=>{setColor('green')}}>Green</button>
            <button onClick={()=>{setColor('blue')}}>Blue</button>
            <button onClick={()=>{setColor('pink')}}>Pink</button>
            <button onClick={()=>{setColor('orange')}}>Orange</button>
        </div>
    );
}

In this code:

console.log('render');

useEffect(() => {
    console.log('Render useEffect');
    document.body.style.backgroundColor = color;
}, [color]);

console.log('Last Line Executed');
  • console.log('render') → Runs during the rendering phase.

  • console.log('Last Line Executed')Runs after all synchronous code in the component finishes.

  • Then React calls useEffect → because effects run after the render phase (non-blocking).

👉 This ensures the UI is updated first, then side effects (like DOM manipulation) are applied.

render
Last Line Executed
Render useEffect

Dependencies ([color])

  • The dependency array controls when the effect runs:

    • [] → Runs only once (on mount).

    • [color] → Runs every time color changes.

    • No array → Runs after every render (bad for performance).


Why do DOM manipulations go inside useEffect?

  • React's render updates the Virtual DOM first.

  • If you manipulate the real DOM inside the render function, it can conflict with React’s updates.

  • useEffect runs after rendering, so DOM is stable, and manipulations won't break React’s rendering cycle.


What is React.memo?

  • React.memo is a higher-order component (HOC) used to optimize functional components.

  • It prevents unnecessary re-renders when a component’s props haven't changed.

  • Works similarly to PureComponent for class components.


How does it work?

When a parent component re-renders:

  1. Normally, all its child components also re-render, even if their props remain the same.

  2. Wrapping a component in React.memo() makes React compare the previous and new props:

    • If props are the same → React skips re-rendering the child.

    • If props changedChild re-renders.


Example

// Parent component
function App() {
    let [count, setCount] = useState(0);
    return (
        <div>
            <div>
                <h1>Count: {count}</h1>
                <button onClick={() => setCount(count + 1)}>Increment</button>
                <button onClick={() => setCount(count - 1)}>Decrement</button>
            </div>
            <Colorful />
        </div>
    );
}
// Child component

function Colorful () {
    let[color, setColor] = useState('black');
    console.log('render');

    useEffect(()=>{
        console.log('Render useEffect');
        document.body.style.backgroundColor = color;
    }, [color]);

    console.log('Last Line Executed');

    return (
        <div>
            <button onClick={()=>{setColor('red')}}>Red</button>
            <button onClick={()=>{setColor('green')}}>Green</button>
            <button onClick={()=>{setColor('blue')}}>Blue</button>
            <button onClick={()=>{setColor('pink')}}>Pink</button>
            <button onClick={()=>{setColor('orange')}}>Orange</button>
        </div>
    );
}

export default React.memo(Colorful);
  • The App component re-renders every time count changes.

  • Without React.memo, the Colorful component would also re-render each time even though it does not depend on count (its state is independent).


Colorful Component with React.memo

export default React.memo(Colorful);
  • Now, React checks if Colorful received any new props from App:

    • Colorful doesn't receive any props.

    • Therefore, its props never change → React skips re-rendering when App updates.

  • The Colorful component will only re-render when:

    1. Its own state changes (color changes).

    2. It receives new props (not applicable here).


function App() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <div>
                <h1>Count: {count}</h1>
                <button onClick={() => setCount(count + 1)}>Increment</button>
                <button onClick={() => setCount(count - 1)}>Decrement</button>
            </div>
            {/* Pass count as props */}
            <Colorful count={count} />
        </div>
    );
}

function Colorful({ count }) {
    const [color, setColor] = useState('black');
    console.log('render');

    useEffect(() => {
        console.log('Render useEffect');
        document.body.style.backgroundColor = color;
    }, [color]);

    console.log('Last Line Executed');

    return (
        <div>
            <h2>Received Count: {count}</h2>
            <button onClick={() => setColor('red')}>Red</button>
            <button onClick={() => setColor('green')}>Green</button>
            <button onClick={() => setColor('blue')}>Blue</button>
            <button onClick={() => setColor('pink')}>Pink</button>
            <button onClick={() => setColor('orange')}>Orange</button>
        </div>
    );
}

export default React.memo(Colorful);

How React.memo works now

  1. Each time count changes in the parent (App), App re-renders.

  2. React.memo checks if the props of Colorful have changed:

    • The count prop does change (new number each time you increment/decrement).

    • Therefore, React will re-render Colorful.

  3. If you click a color button, Colorful re-renders because its own state changes (this happens regardless of React.memo).


When will Colorful skip re-render?

  • If the same props are passed:

      <Colorful count={5} />
    

    and the next render also passes count={5}, React.memo will skip the re-render.

  • But when count actually changes (e.g., 1 → 2), React.memo cannot skip because props are different.


Why is this important?

  • It avoids unnecessary rendering of Colorful when count changes in the parent.

  • This improves performance, especially for components with heavy rendering or complex calculations.


Summary

  • useEffect: Runs after render for side effects (DOM updates, API calls). Runs when dependencies change.

  • React.memo: Skips re-rendering a child if props are unchanged, improving performance.

7
Subscribe to my newsletter

Read articles from Hridoy Chowdhury directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hridoy Chowdhury
Hridoy Chowdhury

I'm a Computer Science & Engineering student exploring the world of Blockchain, Web Development (MERN), and coding interview prep. I write to reinforce my learning and share insights that make complex ideas simpler. Always building, always learning.