← Back to React Course | Chapter 4: State & Event Handling | Lesson 2 of 10

Updating State Correctly

Updating state correctly means always asking React to make the change for you, instead of sneaking in and changing the value yourself.

Never Mutate State Directly

React decides whether to re-render partly by checking if the state value changed. If you mutate the existing value in place instead of creating a new one, React may not detect the change and the UI won't update, even though the underlying data did change.

Note: Always create a new value (a new object, array, or primitive) when updating state, rather than modifying the existing one.

Warning: Something like state.push(newItem) followed by setState(state) often fails to re-render because the array reference didn't change.

Example: Never Mutate State Directly

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function Toggle() {
      const [isOn, setIsOn] = React.useState(false);
      return (
        <button onClick={() => setIsOn(!isOn)}>
          {isOn ? "ON" : "OFF"}
        </button>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<Toggle />);
  </script>
</body>
</html>

State Updates Are Not Instant

Calling a state setter schedules an update for the next render -- it does not change the variable's value immediately within the currently-running function. If you log the state variable right after calling its setter, you'll still see the old value.

Note: If you need to act on the new value immediately, compute it into a separate local variable first, rather than relying on the state variable.

Warning: Code like setCount(count + 1); console.log(count); will log the OLD count, not the new one -- this surprises many beginners.

Example: State Updates Are Not Instant

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function Demo() {
      const [count, setCount] = React.useState(0);
      function handleClick() {
        setCount(count + 1);
        // count here is still the OLD value until the next render
      }
      return <button onClick={handleClick}>Count: {count}</button>;
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<Demo />);
  </script>
</body>
</html>

State Persists Between Renders

Unlike a normal local variable that resets every time a function runs, state declared with useState is preserved by React across re-renders of the same component instance. This is exactly what lets a component remember things like a counter value over time.

Note: Think of state as living outside the function, in a place React manages, even though you declare it inside the function body.

Warning: A plain let variable declared inside a component resets to its initial value on every render -- only useState (or similar Hooks) actually persists.

Example: State Persists Between Renders

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function Counter() {
      const [count, setCount] = React.useState(0);
      // A plain variable here would reset to 0 every render; state does not.
      return <button onClick={() => setCount(count + 1)}>{count}</button>;
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<Counter />);
  </script>
</body>
</html>
Common Mistakes
  1. Mutating an object or array in state directly instead of creating a new copy.
  2. Relying on the state variable's value immediately after calling its setter in the same function.
  3. Forgetting that state updates from the previous render are what you see until the next render happens.
Chapter Summary
  • State should always be updated through its setter function, never mutated directly.
  • React compares old and new state to decide whether to re-render.
  • State updates take effect on the next render, not instantly.
  • Treating state as immutable prevents subtle bugs in React apps.
Browser Support

No browser-specific restrictions -- applies to useState since React 16.8.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.