Cache react-router route
Asked Answered
R

2

9

I have the following code:

<View>
  <Header />
  <List />
</View>

If the user clicks on an edit button in a list item, react-router changes the page from /list to /list/{itemId}. This is a new page. After the user filled in some information and clicks on save, react-router changes back to /list. Now the full /list-page is rerendered!

Is there a way to cache a page, so react can detect the changes and rerender only them instead of the full page again?

It should also be possible to render the page directly (not over /list -> /list/{itemId}) so no modal solution I think.

I'm using react-router-redux v5.0.0-alpha.9 so the solution should be compatible with it. It should also be compatible with react-native and react-dom.

Riplex answered 17/2, 2018 at 15:30 Comment(1)
This isn't a feature supported by react-router directly, but the <Route> component does have a render prop that you could use to show/hide the component rather than the default behavior of unmounting it when navigating to a new route. See github.com/ReactTraining/react-router/issues/4888. I'm not sure what timdorr meant by "that will have unintended side effects most likely" but I imagine that in cases like this it should be fine (unless you're doing it all over the place).Boche
A
6

If you are working with react-router

Component can not be cached while going forward or back which lead to losing data and interaction while using Route

Component would be unmounted when Route was unmatched

After reading source code of Route we found that using children prop as a function could help to control rendering behavior.

Hiding instead of Removing would fix this issue.

I am already fixed it with my tools react-router-cache-route

Usage

Replace <Route> with <CacheRoute>

Replace <Switch> with <CacheSwitch>


If you want real <KeepAlive /> for React

I have my implementation react-activation

Online Demo

Usage

import KeepAlive, { AliveScope } from 'react-activation'

function App() {
  const [show, setShow] = useState(true)

  return (
    <AliveScope>
      <button onClick={() => setShow(show => !show)}>Toggle</button>
      {show && (
        <KeepAlive>
          <Test />
        </KeepAlive>
      )}
    </AliveScope>
  )
}

enter image description here

The implementation principle is easy to say.

Because React will unload components that are in the intrinsic component hierarchy, we need to extract the components in <KeepAlive>, that is, their children props, and render them into a component that will not be unloaded.

Appalling answered 16/9, 2019 at 8:46 Comment(0)
I
2

Got into same problem recently and my initial nudge was to use something like react-router-cache-route or another package.

But main disadvantage of third-party packages is that they are usually slow to support new react-router versions and source code seemed to me overly complex for this problem.

So I researched further and stumbled on this suggestion from Dan Abramov and ended up with solution like this:

function App() {
  const itemId = useParams<{ itemId: string }>();

  return (
    <Switch>
       ...
       <Route
          exact
          path={['some-list-route', 'some-list-route/:itemId']}
       >
          <List style={itemId ? { display: 'none' } : undefined} />
          {itemId && <ListItem />}
       </Route>
       ...
    </Switch>
  )
}

We keep both list and item routes under single Route component to have control over their mounting behaviour. When we get item route — we hide our <List /> (but it stays mounted, "kept-alive") and conditionally mount <ListItem />.

We can also pass itemId to <List /> as a prop, if we need more control, like conditionally trigger data fetching only for list route.

Iiette answered 27/1, 2022 at 9:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.