Artur
Artur

Reputation: 628

React router remounts children while changing routes (without reconciliation)

I have a situation where few routes consists of the same component and I want to preserve its state, but with this behavior it's fairly impossible.

Live example (look at seconds elapsed): https://csb-43lp1km647-nkiinaibit.now.sh

CodeSandbox: https://codesandbox.io/s/43lp1km647

Of course I could save and restore state of timer while changing routes, but my app has an infinite CSS transition as a background (not on every route).

I was trying adding keys, even portal, but it still remounts everything.

Is there any way to enforce typical React's reconciliation (remount only elements that have to be remounted)?

Update: Just found out that <Switch /> will render everything until it finds element that matches, and that could be either a certain <Route /> or any element except it's inside <Fragment />.

Code:

<Router history={history}>
  <Switch>
    <Route path="/third" component={RouteWithoutTimer} />
    <React.Fragment>
      <Timer />
      <Route exact path="/" component={FirstRoute} />
      <Route path="/second" component={SecondRoute} />
    </React.Fragment>
  </Switch>
</Router>

Edit React router remounts children while changing routes

Or with wildcard route:

<Router history={history}>
  <Switch>
    <Route path="/third" component={RouteWithoutTimer} />
    <React.Fragment>
      <Timer />
      <Switch>
        <Route exact path="/" component={FirstRoute} />
        <Route component={NotFoundRoute} />
      </Switch>
    </React.Fragment>
  </Switch>
</Router>

It's not a solution I was looking for, but it works fine.

Upvotes: 10

Views: 676

Answers (2)

Ozgur
Ozgur

Reputation: 3766

This is where the state management libraries come to scene.

In React, states are per component and has lifecycle of it's owned component, so when router destructs/remounts the component, you lose all the data. I advice you to migrate your state to a state management library like redux as soon as possible. Otherwise your code can be mess if this is a medium-sized application.

Upvotes: 2

Roy Wang
Roy Wang

Reputation: 11260

You are mounting a new instance of Timer in each route.
If you want to share a component across routes, you should lift it to the parent component of the routes:

<React.Fragment>
  <Timer />
  <Switch>
    <Route exact path="/" component={FirstRoute} />
    <Route path="/second" component={SecondRoute} />
  </Switch>
</React.Fragment>

Edit React router remounts children while changing routes

Upvotes: 2

Related Questions