Ills
Ills

Reputation: 525

React state (hook) resets on re-render while using switch

See codebox. https://codesandbox.io/s/component-issue-snj24?file=/src/App.js:0-1254

Whenever Main re-renders, the current component rendered conditionally though the switch will have its state reset const [value, setValue] = React.useState(0).

If I took any of the components (comp1, comp2, comp3) and inserted it into the main return (replacing ) this does not occur.

-- Update As noticed by Dennis, its due to function being nested. Alternative https://codesandbox.io/s/component-issue-forked-uejy1

Upvotes: 1

Views: 2611

Answers (3)

Karson Jo
Karson Jo

Reputation: 1712

I had this problem recently. As Dennis said, it is due to function redefinition. Eventually I figured out that this problem could be solved by a very minor change.

Just change the JSX instantiation to a JSX expression (General function call):

from

<RenderActivePage />

to

{ RenderActivePage() }

Because it's not instantiated using JSX, React will not manage the lifecycle of the RenderActivePage element itself. That is, it won't treat RenderActivePage as an element, but will simply output the result of this function.

This approach actually has the same effect as inlining an expression in return:

return (
    <div className="App">
      <h2>Pressing "update main component" resets child state</h2>
      <button onClick={() => setValue(value + 1)}>update main component</button>
      {(() => {
        switch (activePage) {
          case 0:
            return <Comp1 />;
          case 1:
            return <Comp2 />;
          default:
            return <div></div>;
        }
      })()}
    </div>
  );

Upvotes: 0

Amox Cillin
Amox Cillin

Reputation: 53

I know this was already answered a while ago but the answer helped me solve my problem and so just wanted to add my 2 cents. It seems like when a child component is inside any condition in a parent, for example :

{ isTrue && <ChildComponent/> } 
// or  
{ isTrue ? <ChildComponent1/>  : <ChildComponent2/> }

or a switch like above then the child component is unmounted and re-mounted on every re-render of the parent. This ends up resetting the local state in the child component on every re-render of the parent. To solve this problem, I ended up passing the condition to the child as a prop i.e <ChildComponent isTrue={isTrue} /> and then conditionally rendering inside the child.

Upvotes: 2

Dennis Vash
Dennis Vash

Reputation: 53944

The component does not "re-render" it unmounts, because you declared RenderActivePage in function's body, on every render it re-assigned, meaning it re-mounts on every render.

export default function App() {
  // remounts on every render
  const RenderActivePage = () => {
    switch (activePage) {
      case 0:
        return <Comp1 />;
      case 1:
        return <Comp2 />;
      default:
        return <div></div>;
    }
  };

  return (
    <div className="App">
      <RenderActivePage />
    </div>
  );
}

Upvotes: 1

Related Questions