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

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 *