Home > other >  How to prevent a react component remount because of wrapper added/removed
How to prevent a react component remount because of wrapper added/removed

Time:12-17

Consider the following example:

const SomeComponent = ({ id }) => {
  useEffect(() => {
    console.log("mounted", id);
  }, []);

  return null;
};

const App = () => {
  const [outside, setOutside] = useState(false);

  const component = <SomeComponent id="willrerender" key="willrerender" />;
  const otherComponent = <SomeComponent id="wontrerender" key="wontrerender" />;

  const handleClick = () => {
    setOutside((outside) => {
      console.log("setting", outside ? "outside" : "inside");
      return !outside;
    });
  };

  return (
    <div>
      <button onClick={handleClick}>trigger</button>
      {outside ? component : <div>{component}</div>}
      {otherComponent}
    </div>
  );
};

And playground link.

I was expecting that the component (SomeComponent in this case) won't remount just because it's wrapped in an another component (div in this case) - but only update. I would like to prevent this from happening.

The above example shows that if you click the button, it will make willrerender remount, while wontrerender won't rerender.

How could I do that the component "stays" the same (doesn't remount, keeps state) regardless of its parent?

CodePudding user response:

I don't believe this is possible. This just isn't how React works.

React renders all the JSX to a virtual DOM, then compares to the previously rendered DOM, and then mounts/unmounts as necessary from there.

 <SomeComponent id="willrerender" key="willrerender" />;

desugars to:

React.createElement(SomeComponent, { id: "willrerender", key: "willrerender" });

Note that your functional component is just passed to React, along with the props. I'm fairly sure that your component is not fully rendered at this point. Instead React just keeps the component function and its props in the virtual DOM.

Then after everything is rendered and taken its spot in that virtual DOM, React crawls the freshly new tree and mounts/unmounts things as necessary where they differ from the previous render of the virtual DOM.

And state is kept at a particular location in this virtual DOM. So if you change the path to that component, you make it a new "instance" of that component*.

This all means that:

const const component = <SomeComponent id="willrerender" key="willrerender" />;

return <>
  <div>{component}</div>
  <div>{component}</div>
  <div>{component}</div>
</>

Is actually indistinguishable from:

return <>
  <div><SomeComponent id="willrerender" key="willrerender" /></div>
  <div><SomeComponent id="willrerender" key="willrerender" /></div>
  <div><SomeComponent id="willrerender" key="willrerender" /></div>
</>

React is declarative. That means show it how you want the page to look, and then let React manage the details to comply. So for the most part, managing when a component mounts/unmounts should not be your responsibility.


* The exception here is siblings differentiated by keys. This is one common case that React knows it needs to handle. If you change the order of items, the key helps react keep track of which component got moved where. But this only works for siblings of a node. If you move the component vertically in the tree there is nothing in React that will tell React the identity of a component.

  • Related