Home > Enterprise >  React resetting and preserving state?
React resetting and preserving state?

Time:12-07

By default, React preserves state of a component while it stays at the same position. Usually, this is exactly what you want, so it makes sense as the default behavior.

Examples are taken from the new enter image description here


Example 2

{isPlayerA &&
  <Counter person="Taylor" />
}
{!isPlayerA &&
  <Counter person="Sarah" />
}

In this case, the rendering is not interrelated, it is just that the variable deciding the rendering is same and thus you can say both are interrelated.

React will check all such conditions, no matter what to do final rendering. Even if it is like this

For example:

{isPlayerA &&
  <Counter person="Taylor" />
}
{!isPlayerA &&
  <Counter person="Sarah" />
}
{isPlayerB &&
  <Counter person="Jason" />
}
{!isPlayerB &&
  <Counter person="Tyler" />
}

Here, react will check all the conditions, from 1 to 4, and the one who matches will be shown. If playerA === true, it will still check for !playerA and similarly for playerB as well.

As in the image, React is just striking out the elements that are not matching the conditions, there is not any kind of relation in between them.

enter image description here

CodePudding user response:

I believe it's because React treats the definition of both Counter as different elements through shallow equality. Remember in JS, {} === {} will equal false.

On the render where we changed players, it sees that the counter component of the previous render to be of a different instantiation through that if switch.

To preserve the position, you may add a key (should be the same value) on both components if you want the desired effect of preserving the state, or you know, just toggle the person props.

CodePudding user response:

This has entirely to do with the node position within the tree hierarchy: if the same component or node type is not rendered at exactly the same position in the tree, React will consider it to be different. Other answers already cover this, so I'll focus on another important part:

In the reconciliation section of the React docs, the nature of this dilemma is discussed, and a solution is presented: the key attribute.

In order to solve this issue, React supports a key attribute. When children have keys, React uses the key to match children in the original tree with children in the subsequent tree.

I won't copy and paste the entire documentation here, but you can read it at https://reactjs.org/docs/reconciliation.html#keys

Here is an example which demonstrates using a common key to help React maintain state across renders when the element position changes among its siblings:

body {
  font-family: sans-serif;
  padding-bottom: 2rem;
}

button {
  display: block;
  margin: 0.5rem 0;
}

code {
  background-color: hsla(0, 0%, 50%, 0.15);
  padding: 0.2em;
}
<div id="root"></div><script src="https://unpkg.com/[email protected]/umd/react.development.js"></script><script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="react">

const {useState} = React;

function Counter ({person}) {
  const [count, setCount] = useState(0);
  return (
    <div>
      <div>{person}</div>
      <button onClick={() => setCount(c => c   1)}>Count: {count}</button>
    </div>
  );
}

function SameNodePosition () {
  const [isFirstPlayer, setIsFirstPlayer] = useState(true);
  const togglePlayer = () => setIsFirstPlayer(bool => !bool);
  return (
    <div>
      <h2>Same node position</h2>
      {
        isFirstPlayer ? <Counter person="Taylor" /> : <Counter person="Sarah" />
      }
      {/* Can also be written like this:
        <Counter person={isFirstPlayer ? 'Taylor' : 'Sarah'} />
      */}
      <button onClick={togglePlayer}>Toggle player</button>
    </div>
  );
}

function DifferentNodePosition () {
  const [isFirstPlayer, setIsFirstPlayer] = useState(true);
  const togglePlayer = () => setIsFirstPlayer(bool => !bool);
  return (
    <div>
      <h2>Different node position</h2>
      { isFirstPlayer && <Counter person="Taylor" /> }
      { !isFirstPlayer && <Counter person="Sarah" /> }
      <button onClick={togglePlayer}>Toggle player</button>
    </div>
  );
}

function DifferentSiblingNodePositionWithSameKey () {
  const [isFirstPlayer, setIsFirstPlayer] = useState(true);
  const togglePlayer = () => setIsFirstPlayer(bool => !bool);
  return (
    <div>
      <h2>Different sibling node position with same <code>key</code></h2>
      { isFirstPlayer && <Counter key="playerCounter" person="Taylor" /> }
      { !isFirstPlayer && <Counter key="playerCounter" person="Sarah" /> }
      <button onClick={togglePlayer}>Toggle player</button>
    </div>
  );
}

function DifferentNonSiblingNodePositionWithSameKey () {
  const [isFirstPlayer, setIsFirstPlayer] = useState(true);
  const togglePlayer = () => setIsFirstPlayer(bool => !bool);
  return (
    <div>
      <h2>Different non-sibling node position with same <code>key</code></h2>
      { isFirstPlayer && <Counter key="playerCounter" person="Taylor" /> }
      <div>
        { !isFirstPlayer && <Counter key="playerCounter" person="Sarah" /> }
      </div>
      <button onClick={togglePlayer}>Toggle player</button>
    </div>
  );
}

function Example () {
  return (
    <div>
      <SameNodePosition />
      <DifferentNodePosition />
      <DifferentSiblingNodePositionWithSameKey />
      <DifferentNonSiblingNodePositionWithSameKey />
    </div>
  );
}

ReactDOM.render(<Example />, document.getElementById('root'));

</script>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related