react-fiber-keep-alive
Version:
Keep-Alive for React DOM
226 lines (175 loc) • 6.58 kB
Markdown
# Keep-Alive for `React DOM`
[](http://npm.im/react-fiber-keep-alive)
[](https://www.npmjs.com/package/react-fiber-keep-alive)
[](https://www.typescriptlang.org/)
[](https://github.com/shenjunru/react-fiber-keep-alive/blob/main/LICENSE)
`<KeepAlive>` is a component that maintains component state and avoids repeated re-rendering.
## ✨ Features
- [x] Only based on `React Fiber` and `React 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
```bash
npm install --save react-fiber-keep-alive
```
## 🔨 Usage
```JavaScript
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.
```JSX
<KeepAlive.Provider value={container}>
```
- Must be the root container of `render()`.
- If not provided, `keep-alive` will be disabled.
- Wrap your component with `<KeepAlive>`
```JSX
<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()`.
```JavaScript
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.
```JavaScript
import { useIgnoreKeepAlive } from 'react-fiber-keep-alive';
const ignoreCache = useIgnoreKeepAlive();
ignoreCache(`unique-key`);
```
- If the `render()` of class component has side effects.
```JavaScript
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.
```JavaScript
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)
```TSX
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 in `ReactDOM.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-router v5](https://codesandbox.io/s/keep-alive-react-router-example-hfbbi7)
### 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](https://github.com/shenjunru/react-fiber-keep-alive/issues).
## 📄 License
Copyright © 2022 [**Shen Junru**](https://github.com/shenjunru) • [**MIT license**](LICENSE).