React DOM
Keep-Alive for
<KeepAlive>
is a component that maintains component state and avoids repeated re-rendering.
✨ Features
- [x] Only based on
React Fiber
andReact Hooks
. - [x] Triggers original class component life circle.
- [x] Triggers original effect hooks.
- [x] Supports context updates.
- [x] Supports multiple
keep-alive
. - [x] Supports
react-dom
v16.8+. - [x] Supports
react-dom
v17. - [x] Supports
react-dom
v18.
📦 Installation
npm install --save react-fiber-keep-alive
🔨 Usage
import React from 'react';
import ReactDOM from 'react-dom';
import KeepAlive from 'react-fiber-keep-alive';
const root = document.getElementById('root');
ReactDOM.render((
<KeepAlive.Provider value={root}>
...
<KeepAlive name="test">
<YourComponent />
</KeepAlive>
...
</KeepAlive.Provider>
), root);
📝 API
-
Provide
root
container element.<KeepAlive.Provider value={container}>
- Must be the root container of
render()
. - If not provided,
keep-alive
will be disabled.
- Must be the root container of
-
Wrap your component with
<KeepAlive>
<KeepAlive name="unique-key"> <YourComponent /> </KeepAlive>
- prop "name" is a required unique string used to identify the cache.
- prop "ignore" is a optional boolean used to bypass and clear the cache. (since 0.5.0)
- prop "onRead(name: string): void" is a optional callback after cache applied. (since 0.7.0)
- prop "onSave(name: string): void" is a optional callback after cache saved. (since 0.7.0)
-
Wrap your component with
keepLive()
.import { keepAlive } from 'react-fiber-keep-alive'; const NewComponent = keepAlive(YourComponent, (props) => { // props: the income props for `<YourComponent>` // you can use react hooks here return `unique-key`; // or return { name: `unique-key`, // other props for `<KeepAlive>` }; });
-
Hook:
useIgnoreKeepAlive()
returns a cache cleaner function.import { useIgnoreKeepAlive } from 'react-fiber-keep-alive'; const ignoreCache = useIgnoreKeepAlive(); ignoreCache(`unique-key`);
-
If the
render()
of class component has side effects.import { markClassComponentHasSideEffectRender } from 'react-fiber-keep-alive'; markClassComponentHasSideEffectRender(ClassComponent); // Example: class Test extends React.Component { render() { // side effect here, ex: emit event here. return null; } } markClassComponentHasSideEffectRender(Test);
-
If no need to trigger the effect hook while remounting.
import { markEffectHookIsOnetime } from 'react-fiber-keep-alive'; markEffectHookIsOnetime(effectHook); // Example: React.useEffect(markEffectHookIsOnetime(() => { // do something }), []); React.useLayoutEffect(markEffectHookIsOnetime(() => { // do something }), []);
-
KeepAlive.Context
(since 0.7.0)import * as React from 'react'; import KeepAlive, { Context, KeepAliveCache } from 'react-fiber-keep-alive'; import LRUCache from 'lru-cache'; /// Example: Use LRU to manage the cache const YourKeepAliveProvider: React.FC<{ children: React.ReactNode; value: null | HTMLElement; }> = (props) => { const container = props.value; const context: Context = React.useMemo(() => { if (!container) { return []; } const caches = new LRUCache<string, KeepAliveCache>({ max: 10, }); return [container, caches, new Map()]; }, []); return ( <KeepAlive.Context.Provider value={context}> {props.children} </KeepAlive.Context.Provider> ); };
💡 Tips
- Be careful the global side effects. (ex: insert global style)
- Do not use
<KeepAlive>
under the<React.StrictMode>
. - Recursive
<KeepAlive>
handled by top level<KeepAlive>
. - If the
container
changed inReactDOM.createPortal(children, container)
.- All saved sub tree state will be lost.
- Errors from
react-devtools
after<KeepAlive>
remounted.- Try force refresh the components tree. (ex: updates components filter)
🏁 Tested
Examples
React v16.8+ / v17 / v18
- [x]
render(children, container)
- [x]
hydrate(children, container)
React v18 (concurrent mode)
- [x]
createRoot(container).render(children)
- [x]
hydrateRoot(container, children)
Class Component
- [x]
Component.getDerivedStateFromProps()
- [x]
Component.getDerivedStateFromError()
- [x]
instance.componentDidMount()
- [x]
instance.getSnapshotBeforeUpdate()
- [x]
instance.componentDidUpdate()
- [x]
instance.componentWillUnmount()
- [x]
instance.render()
Function Component
- [x]
useContext()
- [x]
useCallback()
- [x]
useEffect()
- [x]
useImperativeHandle()
- [x]
useLayoutEffect()
- [x]
useMemo()
- [x]
useRef()
- [x]
useState()
- [ ]
useDebugValue()
- [ ]
useDeferredValue()
(since v18) - [ ]
useId()
(since v18) - [x]
useInsertionEffect()
(since v18) - [ ]
useSyncExternalStore()
(since v18) - [ ]
useTransition()
(since v18)
Other
- [x]
ReactDOM.createPortal(children, container)
- [x]
React.memo()
- [x]
React.forwardRef()
- [x]
React.lazy()
- [x]
<Suspense>
- [ ]
<Offscreen>
(since v18)
🐛 Issues
If you find a bug, please file an issue on our issue tracker on GitHub.
📄 License
Copyright © 2022 Shen Junru • MIT license.