react-router-hibernate
v0.3.0
Published
Restore previously-unmounted routes -- state and all -- when you return
Downloads
21
Maintainers
Readme
React-Router-Hibernate
A React Router <Switch>
which can leave inactive routes mounted-but-inactive until you navigate back.
Part of React Hibernate
Overview
By defaut, navigating from one <Route>
to another in react-router will unmount the old route's tree.
If you return to it, it will be remounted from scratch.
This library adds <HibernatingSwitch>
and <HibernatingRoute>
: drop-in replacements for <Switch>
and <Route>
which do not immediately unmount components when you navigate away. If you return, the prior tree will be restored,
local state and all.
Example
import { HibernatingSwitch, HibernatingRoute } from 'react-router-hibernate';
// then render:
<HibernatingSwitch maxCacheTime={60000}>
{/* Use the "Hibernating" variants exactly like the standard ones */}
<HibernatingRoute path="/foo" component={...} />
<HibernatingRoute path="/bar" render={...} />
{/* You can mix and match: use them alongside the normal react-router components */}
<Route path="/baz" component={...} />
<Redirect from="/something-else" to="/foo" />
{/* If you have your own custom components, add an isHibernatingRoute prop */}
<MyPrivateRoute path="/secret" component={...} isHibernatingRoute />
</HibernatingSwitch>
Props for HibernatingSwitch
maxCacheSize
(number, default: 5)
Number of subtrees to keep in the cache, including the current one. path
is used for the cache keys.
Set a falsy value to disable.
maxCacheTime
(milliseconds, default: 5 minutes)
Time after which a subtree is removed from the cache. Set a falsy value to disable.
WrapperComponent
(React component, default: none)
A component which wraps all potentially-hibernatable routes. It receives a shouldUpdate
prop.
React-Pauseable-Containers
was created for this.
How it Works
Each route's subtree is rendered via a React-Reverse-Portal, and the portal nodes are stored and rotated via a Limited-Cache.
When you revisit a route which is still in the cache, its prior subtree is reattached. Because the elements and instances were not destroyed, all prior state is still present -- both React state and dom state.
Preventing Extra Work in Inactive Routes
Previously-rendered trees are still mounted when inactive: they're just not attached to the dom.
HibernatingRoute
will suppress the normal render cycle for inactive subtrees, but if a component in the subtree
experiences a state change or context update then it will rerun.
This is fundamental to React and cannot be avoided.
For example: react-redux's useSelector hook works by forcing a rerender outside of the normal render cycle by changing local state -- so suppressing the normal render cycle is not enough.
In most cases this is fine -- inactive subtrees still use minimal resources -- but if the component render is slow or has side effects then you may want to avoid running it at all.
You can do that by setting a WrapperComponent
which halts context updates.
React-Pauseable-Containers
was created for this: you can use its components directly,
compose them together to make your WrapperComponent
, or follow the examples to make your own from scratch.
Roadmap
- [x] Proof of concept
- [x] Project scaffolding
- [x] Core functionality
- [x] Tests
- [x] Demos
- [x] Monorepo
- [x] Initial release
- [ ] Add
useHibernatingEffect
hook (successfully prototyped) - [ ] Add
maxCacheTime
override per-route (successfully prototyped) - [ ] Explore: Options to better control which/when to add a subtree
- [ ] Explore: React-Router v6
Known Issues
"Cannot update a component from inside the function body of a different component" warning in React 16.13+
- This will be addressed as part of supporting React-Router v6, when subtree activation will need to be done via a component instead of a callback.