Passing an object in setState() without creating a new reference

Loading

In React, when working with object state, you need to be careful about reference equality. Here’s how to properly update object state without unnecessarily creating new object references:

The Problem

When you do this with class components:

this.setState({
  user: {
    ...this.state.user,
    name: 'New Name'
  }
});

Or with function components:

setUser(prev => ({
  ...prev,
  name: 'New Name'
}));

You’re creating a new object reference each time, which can cause unnecessary re-renders if other parts of your state haven’t changed.

Optimization Techniques

1. Class Components: Functional setState

this.setState(prevState => {
  // Only create new object if name actually changed
  if (prevState.user.name === 'New Name') return null;

  return {
    user: {
      ...prevState.user,
      name: 'New Name'
    }
  };
});

2. Function Components: useMemo/useCallback

const [user, setUser] = useState({ name: 'John', age: 30 });

const updateName = useCallback((newName) => {
  setUser(prev => {
    if (prev.name === newName) return prev;
    return { ...prev, name: newName };
  });
}, []);

3. Using Immer for Complex State

import produce from 'immer';

// Class component
this.setState(produce(draft => {
  draft.user.name = 'New Name';
}));

// Function component
setUser(produce(draft => {
  draft.name = 'New Name';
}));

4. Selective Updates

// Only update specific properties
this.setState({
  'user.name': 'New Name'
}, () => {
  // Optional callback
});

When to Worry About References

Only optimize when:

  • Your object is very large
  • You’re passing the object to many child components
  • You’re using the object in dependency arrays
  • Performance issues are actually observed

Best Practices

  1. Don’t prematurely optimize – React is already efficient at reconciliation
  2. Use Immer for complex nested state – It handles references optimally
  3. Consider breaking up state – Maybe you don’t need one large object
  4. Use multiple state variables in function components instead of one big object
// Instead of:
const [user, setUser] = useState({ name: '', age: 0 });

// Consider:
const [name, setName] = useState('');
const [age, setAge] = useState(0);

Remember that in most cases, the spread operator pattern is perfectly fine and readable. Only optimize object references when you have measurable performance issues.

Leave a Reply

Your email address will not be published. Required fields are marked *