DEV Community

Muhammad Muneeb Ur Rehman
Muhammad Muneeb Ur Rehman

Posted on

How React Preserve and Reset State

State is isolated between components. React keeps track of which state belongs to which component based on their place in the UI tree. You can control when to preserve state and when to reset it between re-renders.

The UI tree
Browsers use many tree structures to model UI. The DOM represents HTML elements, the CSSOM does the same for CSS. There’s even an Accessibility tree!

React also uses tree structures to manage and model the UI you make. React makes UI trees from your JSX. Then React DOM updates the browser DOM elements to match that UI tree. (React Native translates these trees into elements specific to mobile platforms.)

From components, React creates a UI tree which React DOM uses to render the DOM
Note: Open Image for Best View and Understanding.

From components, React creates a UI tree which React DOM uses to render the DOM

State is tied to a position in the tree
When you give a component state, you might think the state “lives” inside the component. But the state is actually held inside React. React associates each piece of state it’s holding with the correct component by where that component sits in the UI tree.

Let's see it with the help of an example. Assume, we have a <Counter /> JSX tag, but it’s rendered at two different positions:

import { useState } from 'react';

export default function App() {
  const counter = <Counter />;
  return (
    <div>
      {counter}
      {counter}
    </div>
  );
}

function Counter() {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);

  let className = 'counter';
  if (hover) {
    className += ' hover';
  }

  return (
    <div
      className={className}
      onPointerEnter={() => setHover(true)}
      onPointerLeave={() => setHover(false)}
    >
      <h1>{score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

React tree
Note: Open Image for Best View and Understanding.

These are two separate counters because each is rendered at its own position in the tree. You don’t usually have to think about these positions to use React, but it can be useful to understand how it works.

In React, each component on the screen has fully isolated state. For example, if you render two Counter components side by side, each of them will get its own, independent, score and hover states.

Try running the code and clicking both counters and notice they don’t affect each other:

import { useState } from 'react';

export default function App() {
  return (
    <div>
      <Counter />
      <Counter />
    </div>
  );
}

function Counter() {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);

  let className = 'counter';
  if (hover) {
    className += ' hover';
  }

  return (
    <div
      className={className}
      onPointerEnter={() => setHover(true)}
      onPointerLeave={() => setHover(false)}
    >
      <h1>{score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

As you can see, when one counter is updated, only the state for that component is updated:

Updating state
Note: Open Image for Best View and Understanding.

React will keep the state around for as long as you render the same component at the same position. To see this, increment the counter, then remove it by unchecking “Render the counter” checkbox, and then add it back by ticking it again:

import { useState } from 'react';

export default function App() {
  const [showB, setShowB] = useState(true);
  return (
    <div>
      {showB && <Counter />} 
      <label>
        <input
          type="checkbox"
          checked={showB}
          onChange={e => {
            setShowB(e.target.checked)
          }}
        />
        Render the counter
      </label>
    </div>
  );
}

function Counter() {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);

  let className = 'counter';
  if (hover) {
    className += ' hover';
  }

  return (
    <div
      className={className}
      onPointerEnter={() => setHover(true)}
      onPointerLeave={() => setHover(false)}
    >
      <h1>{score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Deleting a component
Note: Open Image for Best View and Understanding.

When you tick “Render the counter”, a new Counter and its state are initialized from scratch (score = 0) and added to the DOM.

Adding a component

Note: Open Image for Best View and Understanding.
React preserves a component’s state for as long as it’s being rendered at its position in the UI tree. If it gets removed, or a different component gets rendered at the same position, React discards its state.

Source: https://react.dev/learn/preserving-and-resetting-state

Top comments (0)