Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/CJY0208/react-activation
Hack <KeepAlive /> for React
https://github.com/CJY0208/react-activation
cache cache-control keep-alive keepalive offscreen react
Last synced: 3 months ago
JSON representation
Hack <KeepAlive /> for React
- Host: GitHub
- URL: https://github.com/CJY0208/react-activation
- Owner: CJY0208
- License: mit
- Created: 2019-09-04T10:35:13.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2024-08-16T10:27:19.000Z (5 months ago)
- Last Synced: 2024-10-19T19:31:31.697Z (3 months ago)
- Topics: cache, cache-control, keep-alive, keepalive, offscreen, react
- Language: JavaScript
- Homepage: https://www.npmjs.com/package/react-activation
- Size: 1.97 MB
- Stars: 1,804
- Watchers: 16
- Forks: 141
- Open Issues: 72
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome - CJY0208/react-activation - Hack \<KeepAlive /> for React (JavaScript)
- awesome-web - react-activation
- awesome-star-libs - CJY0208 / react-activation
README
### NOTICE
- DO NOT use ``
- (React v18+) DO NOT use `ReactDOMClient.createRoot`, use `ReactDOM.render` instead, https://github.com/CJY0208/react-activation/issues/225#issuecomment-1311136388# React Activation
[![size](https://img.shields.io/bundlephobia/minzip/[email protected])](https://bundlephobia.com/result?p=react-activation@latest)
[![dm](https://img.shields.io/npm/dm/react-activation.svg)](https://github.com/CJY0208/react-activation)
![](https://komarev.com/ghpvc/?username=cjy0208-react-activation&label=VIEWS)English | [中文说明](./README_CN.md)
**HACK Implementation** of the `` function in `Vue` For `React`
Please also pay attention to official support [``](https://github.com/reactwg/react-18/discussions/19) in `React 18.x`
---
More stable `` function with `babel` pre-compilation
[Online Demo](https://codesandbox.io/s/affectionate-beaver-solkt)
---
## More examples
- [Closable tabs with `react-router`](https://codesandbox.io/s/keguanbideyifangwenluyou-tab-shilikeanluyoucanshufenduofenhuancun-ewycx)
- [Closable tabs with `umi`](https://codesandbox.io/s/umi-keep-alive-tabs-demo-knfxy)
- [Using Animation with `react-router`](https://codesandbox.io/s/luyouzhuanchangdonghuashili-jdhq1)---
## Compatibility
- React v16 / v17 / v18
- Preact v10+
- Compatible with SSR
---
## Install
```bash
yarn add react-activation
# or
npm install react-activation
```---
## Usage
#### 1. (Optional, Recommended) Add `react-activation/babel` plugins in `.babelrc`
[Why is it needed?](https://github.com/CJY0208/react-activation/issues/18#issuecomment-564360695)
The plugin adds a `_nk` attribute to each JSX element during compilation to help the `react-activation` runtime **generate an unique identifier by render location** base on [`react-node-key`](https://github.com/CJY0208/react-node-key).
```javascript
{
"plugins": [
"react-activation/babel"
]
}
```**(0.11.0+)** If you don't want to use Babel, it is recommended that each `` declare a globally unique and invariant `cacheKey` attribute to ensure the stability of the cache, as follows:
```jsx
```
#### 2. Wrap the components that need to keep states with ``
Like the `` component in the example
```javascript
// App.jsimport React, { useState } from 'react'
import KeepAlive from 'react-activation'function Counter() {
const [count, setCount] = useState(0)return (
count: {count}
setCount(count => count + 1)}>Add
)
}function App() {
const [show, setShow] = useState(true)return (
setShow(show => !show)}>Toggle
{show && (
)}
)
}export default App
```#### 3. Place the `` outer layer at a location that will not be unmounted, usually at the application entrance
Note: When used with `react-router` or `react-redux`, you need to place `` inside `` or ``
```javascript
// index.jsimport React from 'react'
import ReactDOM from 'react-dom'
import { AliveScope } from 'react-activation'import App from './App'
ReactDOM.render(
,
document.getElementById('root')
)
```---
## Lifecycle
`ClassComponent` works with `withActivation` decorator
Use `componentDidActivate` and `componentWillUnactivate` to correspond to the two states of "activate" and "unactivate" respectively.
`FunctionComponent` uses the `useActivate` and `useUnactivate` hooks respectively
```javascript
...
import KeepAlive, { useActivate, useUnactivate, withActivation } from 'react-activation'@withActivation
class TestClass extends Component {
...
componentDidActivate() {
console.log('TestClass: componentDidActivate')
}componentWillUnactivate() {
console.log('TestClass: componentWillUnactivate')
}
...
}
...
function TestFunction() {
useActivate(() => {
console.log('TestFunction: didActivate')
})useUnactivate(() => {
console.log('TestFunction: willUnactivate')
})
...
}
...
function App() {
...
return (
{show && (
)}
)
}
...
```---
## Cache Control
### Manually control the cache
1. Add the `name` attribute to the `` tag that needs to control the cache.
2. Get control functions using `withAliveScope` or `useAliveController`.
- **drop(name)**: (`drop` can only be used for nodes in the cache state. If the node is not cached but needs to clear the cache state, please use `refresh`)
Unload the `` node in cache state by name. The name can be of type `String` or `RegExp`. Note that only the first layer of content that hits `` is unloaded and will not be uninstalled in ``. Would not unload nested ``.
- **dropScope(name)**: (`dropScope` can only be used for nodes in the cache state. If the node is not cached but needs to clear the cache state, please use `refreshScope`)
Unloads the `` node in cache state by name. The name optional type is `String` or `RegExp`, which will unload all content of ``, including all `` nested in ``.
- **refresh(name)**:
Refresh the `` node in cache state by name. The name can be of type `String` or `RegExp`. Note that only the first layer of content that hits `` is refreshed and will not be uninstalled in ``. Would not refresh nested ``.
- **refreshScope(name)**:
Refresh the `` node in cache state by name. The name optional type is `String` or `RegExp`, which will refresh all content of ``, including all `` nested in ``.
- **clear()**:
will clear all `` in the cache
- **getCachingNodes()**:
Get all the nodes in the cache
```javascript
...
import KeepAlive, { withAliveScope, useAliveController } from 'react-activation'
......
...
...
...
......
function App() {
const { drop, dropScope, clear, getCachingNodes } = useAliveController()useEffect(() => {
drop('Test')
// or
drop(/Test/)
// or
dropScope('Test')clear()
})return (
...
)
}
// or
@withAliveScope
class App extends Component {
render() {
const { drop, dropScope, clear, getCachingNodes } = this.propsreturn (
...
)
}
}
...
```---
### Automatic control cache
Add the `when` attribute to the `` tag that needs to control the cache. The value is as follows
#### When the `when` type is `Boolean`
- **true**: Cache after uninstallation
- **false**: Not cached after uninstallation```javascript
```
#### When the `when` type is `Array`
The **1th** parameter indicates whether it needs to be cached at the time of uninstallation.
The **2th** parameter indicates whether to unload all cache contents of ``, including all `` nested in ``.
```javascript
// For example:
// The following indicates that it is not cached when uninstalling
// And uninstalls all nested ``...
...
...
...
...```
#### When the `when` type is `Function` (**Recommended**)
The return value is the above `Boolean` or `Array`, which takes effect as described above.
The final calculation time of `when` is adjusted to `componentWillUnmount` lifecicle of ``, the problem that most of the `when` do not achieve the expected effect can be avoided.
```jsx
true}>
[false, true]}>
```---
## Multiple Cache
Under the same parent node, `` in the same location will use the same cache by default.
For example, with the following parameter routing scenario, the `/item` route will be rendered differently by `id`, but only the same cache can be kept.
```javascript
(
)}
/>
```Similar scenarios, you can use the `id` attribute of `` to implement multiple caches according to specific conditions.
```javascript
(
)}
/>
```---
## Save Scroll Position (`true` by default)
`` would try to detect scrollable nodes in its `children`, then, save their scroll position automaticlly before `componentWillUnactivate` and restore saving position after `componentDidActivate`
If you don't want `` to do this thing, set `saveScrollPosition` prop to `false`
```javascript
```
If your components share screen scroll container, `document.body` or `document.documentElement`, set `saveScrollPosition` prop to `"screen"` can save sharing screen container's scroll position before `componentWillUnactivate`
```javascript
```
---
## Principle
Pass the `children` attribute of `` to `` and render it with ``
After rendering ``, the content is transferred to `` through `DOM` operation.
Since `` will not be uninstalled, caching can be implemented.
[Simplest Implementation Demo](https://codesandbox.io/s/zuijian-react-keepalive-shixian-ovh90)
---
## Breaking Change
1. `` needs to pass children to `` , so the rendering of the real content will be **slower than the normal situation**
Will have a certain impact on the function of strictly relying on the lifecycle order, such as getting the value of `ref` in `componentDidMount`, as follows
```javascript
instance
class Test extends Component {
componentDidMount() {
console.log(this.outside) // will log
console.log(this.inside) // will log undefined
}render() {
return (
{
this.outside = ref
}}
>
Outside KeepAlive
{
this.inside = ref
}}
>
Inside KeepAlive
)
}
}
```The above error in `ClassComponent` can be fixed by using the `withActivation` high-level component
`FunctionComponent` currently has no processing method, you can use `setTimeout` or `nextTick` to delay ref getting behavior
```javascript
instance
@withActivation
class Test extends Component {
componentDidMount() {
console.log(this.outside) // will log
console.log(this.inside) // will log instance
}render() {
return (
{
this.outside = ref
}}
>
Outside KeepAlive
{
this.inside = ref
}}
>
Inside KeepAlive
)
}
}
```2. Destructive impact on `Context`
after `[email protected]` with `[email protected]+`, this question has been automatic fixed
`[email protected]` with `react@17+` you Need to make the following changes to achieve automatic repair
```jsx
import { autoFixContext } from 'react-activation'autoFixContext(
[require('react/jsx-runtime'), 'jsx', 'jsxs', 'jsxDEV'],
[require('react/jsx-dev-runtime'), 'jsx', 'jsxs', 'jsxDEV']
)
```
Versions below `[email protected]` need to be repaired manually, refer to the followingProblem reference: https://github.com/StructureBuilder/react-keep-alive/issues/36
```javascript
{show && (
{(
context // Since the rendering level is broken, the context cannot be obtained here.
) => }
)}
toggle
```Choose a repair method
- Create `Context` using `createContext` exported from `react-activation`
- Fix the affected `Context` with `fixContext` exported from `react-activation`
```javascript
...
import { createContext } from 'react-activation'const { Provider, Consumer } = createContext()
...
// or
...
import { createContext } from 'react'
import { fixContext } from 'react-activation'const Context = createContext()
const { Provider, Consumer } = ContextfixContext(Context)
...
```3. Affects the functionality that depends on the level of the React component, as follows
- [x] [Fix `withRouter/hooks` of react-router](https://github.com/CJY0208/react-activation/issues/77)
- [x] ~~Error Boundaries~~ (Fixed)
- [x] ~~React.Suspense & React.lazy~~ (Fixed)
- [ ] React Synthetic Event Bubbling Failure
- [ ] Other undiscovered features---