Are Objects Inside React Functional Components Always Recreated on Re-Render?

When you define an object inside a React functional component, it is recreated on every re-render. This is because the old values inside its function body are garbage-collected and a new object is created on every re-render. This means that the object:

  1. Has a new reference created in memory on every re-render, and;
  2. Is not equal to the same object from the previous re-render when doing Object.is() or shallow comparison.

For example:

// MyComponent.jsx
import React, { useState, useEffect } from 'react';

export function MyComponent() {
  const [foo, setFoo] = useState('');

  // every time `foo` changes, this will be a different object
  const obj = { a: 1, b: 2, c: 3 };

  useEffect(() => {
    console.log('effect was run');
  }, [obj]);

  return (
    <button onClick={() => setFoo(Math.random())}>Foo</button>
  );
}

In this example, every time the reactive value "foo" changes, it causes the component to re-render, leading the object "obj" to be recreated each time. When this happens, the useEffect callback is run every time as the reference of the "obj" from the previous render is not the same as the current reference in memory.

Compare this to a primitive value declared inside the component body, which will not cause the effect to run on every re-render because primitive values are compared by value rather than by reference. Unlike objects, where a new reference is created on each re-render, the value of primitive remains the same across re-renders.

If you want to maintain the object state that persists across re-renders, you can use the useState() hook. The state object created by useState() is memoized and will persist across re-renders. For example:

// MyComponent.jsx
import React, { useState, useEffect } from 'react';

export function MyComponent() {
  const [foo, setFoo] = useState('');
  const [obj, setObj] = useState({ a: 1, b: 2, c: 3 });

  useEffect(() => {
    console.log('effect was run');
  }, [obj]);

  return (
    <button onClick={() => setFoo(Math.random())}>Foo</button>
  );
}

In this example, the "obj" state will retain its value across re-renders, preventing the effect from running on every re-render, unless you explicitly update it using "setObj()".

As a good practice, it's generally advisable to avoid passing objects directly to the dependency array of React hooks. However, if you must do that, consider memoizing the object (using useMemo() or useState()), or destructure individual values from the object and use them directly in the dependency array.


This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.