skip to Main Content

First of all, sorry for my poor English! (translated by DeepL)

MyComponent has some ‘state’ like a counter and displays it.

In pattern A, when the hiddenFlag is switched, ‘state’ of the first MyComponent is kept.
In pattern B, when the hiddenFlag is switched, ‘state’ of the second MyComponent is kept.

In both cases, the number of MyComponent displayed by hiddenFlag is the same.
How does React detect the same components?

Pattern A:

return hiddenFlag ? (
  <div>
    <MyComponent />
  </div>
) : (
  <div>
    <MyComponent />
    <MyComponent />
  </div>
);

Pattern B:

return (
  <div>
    {!hiddenFlag && <MyComponent />}
    <MyComponent />
  </div>
);

I know that React has two phases "the rendering phase" and "the committing phase" when the state of a component changes. The former phase creates a virtual DOM and the latter rewrites the changed location compared to the current DOM.

Does this mean that when the if branch is calculated in the rendering phase of pattern A, it also determines that the first MyComponent is the same one?

2

Answers


  1. If you are looking to persist a specific component, you can do so with the key prop. In the example below, I have created both of your scenarios, and provided keys to the counter elements. By doing so, in scenario A, I am able to tell react that the 2nd Counter is the one I want to persist.

    const { useState } = React;
    
    function Counter() {
      const [count, setCount] = useState(0);
      return <button onClick={() => setCount(p => p + 1)}>{count}</button>;
    }
    
    function App(props) {
      const [hidden, setHidden] = useState(false);
      return (
        <div className='App'>
          <div>
            <button onClick={() => setHidden(p => !p)}>{ hidden ? "Show" : "Hide" }</button>
          </div>
    <div>Scenario A</div>
          {hidden ? (
            <div>
              <Counter key="1"/>
            </div>
          ) : (
            <div>
              <Counter key="3"/>
              <Counter key="1"/>
            </div>
          )}
    <div>Scenario B</div>
          <div>
            <Counter key="6"/>
            {hidden || <Counter key="7"/>}
          </div>
        </div>
      );
    }
    
    ReactDOM.createRoot(
        document.getElementById("root")
    ).render(
        <App />
    );
    <div id="root"></div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

    In react the key prop is a reserved prop that every component can have. It allows react to track & identify a specific component on the page. It must be used with arrays so that react knows which component of the array is being updated. You should avoid using the array index as the key whenever possible.

    Honestly, I’m a bit surprised that scenario A persists a counter when no key prop is provided. Up until today I would have expected react to treat scenario A as one or two new components.

    Login or Signup to reply.
  2. To my knowledge (I may be mistaken) when the key property is not specified – which is what you should do when the order of components can change, see Scott Z’s answer – React assumes the order of elements in the list of children will be constant each time.

    The statement {!hiddenFlag && <MyComponent />} will either give <MyComponent /> or false depending on the flag, in either case it is seen as the "first" child of the div so the second element that is always the same retains state when the hiddenFlag is toggled.

    In the case of

    hiddenFlag ? (
      <div>
        <MyComponent/>
      </div>
    ) : (
      <div>
        <MyComponent />
        <MyComponent/>
      </div>
    );
    

    The best react can do is assume the order of elements is the same so the first MyComponent retains the state.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search