How to fix Warning: Props should not change between renders in React

ReactINTERMEDIATEMEDIUMUpdated Dec 10, 2025ยท 82 views

This warning indicates that component props are being mutated or recreated on every render, breaking React's optimization mechanisms. It typically occurs when using React.memo or hooks with object/array props that have unstable references.

Quick fix

Fixes Warning: Props should not change between renders

// Problem: Object created on every render
function ParentComponent() {
  return <MemoizedChild config={{ theme: 'dark', size: 'large' }} />;
}

const MemoizedChild = React.memo(({ config }) => {
  return <div>Theme: {config.theme}</div>;
});
  1. Identify components with unstable prop references
  2. Memoize object and array props with useMemo
  3. Wrap callback props with useCallback
  4. Move static values outside component scope
react โ€” error
// Problem: Object created on every render
function ParentComponent() {
return <MemoizedChild config={{ theme: 'dark', size: 'large' }} />;
}
const MemoizedChild = React.memo(({ config }) => {
return <div>Theme: {config.theme}</div>;
});
Warning: Props should not change between renders

What this error means

This warning appears when React detects that props passed to a component are changing their reference between renders even though their content might be the same. React relies on reference equality checks for performance optimizations, particularly with React.memo and hooks like useEffect, useMemo, and useCallback. When objects, arrays, or functions are created inline or defined inside a component's render body, they get a new reference on every render. This breaks React's ability to determine if props have actually changed, causing unnecessary re-renders and hook re-executions. This is particularly problematic with React.memo wrapped components, which only do shallow prop comparisons by default. If a parent component passes a newly created object or function as a prop, the memoized child will re-render every time despite having functionally identical props.

Typical symptoms

  • Component wrapped in React.memo re-renders unnecessarily on every parent render
  • useEffect runs on every render despite seemingly unchanged dependencies
  • Performance issues from excessive re-renders
  • useMemo and useCallback dependencies trigger recalculations constantly
  • Console warnings about dependency arrays in hooks

Common causes

  1. Passing inline objects or arrays as props that get recreated on each render
  2. Defining callback functions inside component body without useCallback
  3. Creating new object/array references in render without useMemo
  4. Mutating props directly instead of creating new references when needed
  5. Using non-primitive values as useEffect dependencies without memoization
  6. Destructuring props that contain objects without stable references

How to fix "Warning: Props should not change between renders"

1Identify components with unstable prop references

Use React DevTools Profiler to identify components that re-render frequently. Look for components wrapped in React.memo or using hooks with object/array dependencies.

tsx
// Problem: Object created on every render
function ParentComponent() {
  return <MemoizedChild config={{ theme: 'dark', size: 'large' }} />;
}

const MemoizedChild = React.memo(({ config }) => {
  return <div>Theme: {config.theme}</div>;
});

Check your console for warnings about exhaustive dependencies in useEffect or other hooks.

2Memoize object and array props with useMemo

Wrap object and array props in useMemo to maintain stable references across renders. Only recreate them when their actual values change.

tsx
import { useMemo } from 'react';

function ParentComponent() {
  // Good: Stable reference unless dependencies change
  const config = useMemo(() => ({
    theme: 'dark',
    size: 'large'
  }), []); // Empty array = never changes

  return <MemoizedChild config={config} />;
}

// With dynamic values
function ParentWithState({ userPreference }) {
  const config = useMemo(() => ({
    theme: userPreference,
    size: 'large'
  }), [userPreference]); // Only recreate when userPreference changes

  return <MemoizedChild config={config} />;
}
3Wrap callback props with useCallback

Use useCallback to memoize function props and prevent them from being recreated on every render.

tsx
import { useCallback } from 'react';

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

  // Problem: New function reference on every render
  // const handleClick = () => setCount(prev => prev + 1);

  // Good: Stable function reference
  const handleClick = useCallback(() => {
    setCount(prev => prev + 1);
  }, []); // No dependencies needed with functional updates

  return <MemoizedButton onClick={handleClick} />;
}

// With dependencies
function ParentWithDeps({ userId }) {
  const handleSave = useCallback((data) => {
    saveToAPI(userId, data);
  }, [userId]); // Recreate only when userId changes

  return <MemoizedForm onSave={handleSave} />;
}
4Move static values outside component scope

For values that never change, define them outside the component to avoid recreating them on every render.

tsx
// Good: Defined once, stable reference
const DEFAULT_CONFIG = {
  theme: 'dark',
  size: 'large',
  timeout: 5000,
};

const VALIDATION_RULES = ['required', 'email', 'minLength'];

function MyComponent() {
  return (
    <>
      <ConfiguredChild config={DEFAULT_CONFIG} />
      <ValidatedInput rules={VALIDATION_RULES} />
    </>
  );
}

This is the most efficient solution for truly static data.

5Fix useEffect dependency arrays

Ensure all dependencies in useEffect are properly memoized if they're objects, arrays, or functions.

tsx
function MyComponent({ config }) {
  // Problem: Runs on every render if config is unstable
  // useEffect(() => {
  //   applyConfig(config);
  // }, [config]);

  // Solution 1: Memoize the config in parent component
  // Solution 2: Destructure only the values you need
  useEffect(() => {
    const { theme, size } = config;
    applyConfig(theme, size);
  }, [config.theme, config.size]); // Depend on primitives

  // Solution 3: Use a ref for values you don't want to trigger effects
  const configRef = useRef(config);
  useEffect(() => {
    configRef.current = config;
  });

  useEffect(() => {
    // Use configRef.current, no dependency needed
    applyConfig(configRef.current);
  }, []); // Only run once
}
6Use custom comparison function with React.memo

For complex props, provide a custom comparison function to React.memo instead of relying on shallow equality.

tsx
const MemoizedComponent = React.memo(
  ({ config, data }) => {
    return <div>{config.theme} - {data.length} items</div>;
  },
  (prevProps, nextProps) => {
    // Return true if props are equal (skip re-render)
    // Return false if props are different (re-render)
    return (
      prevProps.config.theme === nextProps.config.theme &&
      prevProps.config.size === nextProps.config.size &&
      prevProps.data.length === nextProps.data.length
    );
  }
);

Use this approach when you need deep equality checks or want to compare specific properties.

Advanced notes

Performance Profiling: Before optimizing, use React DevTools Profiler to identify actual performance bottlenecks. Premature memoization can make code harder to read without meaningful benefits.

useMemo and useCallback overhead: These hooks have their own cost. Only use them for expensive computations or when passing props to memoized components. For simple components, re-rendering is often faster than memoization overhead.

Deep equality libraries: For complex nested objects, consider libraries like fast-deep-equal or lodash's isEqual in custom comparison functions, but be aware of the performance cost.

State management solutions: If you're frequently passing complex objects through many component layers, consider using Context API with useMemo, or state management libraries like Redux or Zustand that provide stable references.

Immutability patterns: Always create new object/array references when updating state, never mutate existing ones. Use spread operators, Object.assign, or libraries like Immer for complex updates.

ESLint plugin: Install eslint-plugin-react-hooks to catch exhaustive-deps violations automatically during development.

Frequently asked questions

What does "Warning: Props should not change between renders" mean?
This warning appears when React detects that props passed to a component are changing their reference between renders even though their content might be the same. memo and hooks like useEffect, useMemo, and useCallback. When objects, arrays, or functions are created inline or defined inside a component's render body, they get a new reference on every render.
How do I fix "Warning: Props should not change between renders"?
This warning indicates that component props are being mutated or recreated on every render, breaking React's optimization mechanisms. It typically occurs when using React.memo or hooks with object/array props that have unstable references.
What causes "Warning: Props should not change between renders"?
Passing inline objects or arrays as props that get recreated on each render Defining callback functions inside component body without useCallback Creating new object/array references in render without useMemo
Is the "Warning: Props should not change between renders" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading