Some knowledge about the keep-alive function of react is here (below)
This article follows the interior of the previous article, so it starts from the ninth point
Nine, keep the page scroll
For example, table
on the page contains 100
of data. If we want to see the 100th piece of data, we need to scroll a lot of distance. In many scenarios, this scroll distance must be reserved. of.
The method used here is actually quite traditional. First, a method for handling scrolling is issued in KeepAliveProvider
:
const handleScroll = useCallback(
(cacheId, event) => {
if (catheStates?.[cacheId]) {
const target = event.target
const scrolls = catheStates[cacheId].scrolls
scrolls[target] = target.scrollTop
}
},
[catheStates]
)
Receive and execute in the Keeper
component:
const { dispatch, mount, handleScroll } = useContext(CacheContext)
useEffect(() => {
const onScroll = handleScroll.bind(null, cacheId)
(divRef?.current as any)?.addEventListener?.('scroll', onScroll, true)
return (divRef?.current as any)?.addEventListener?.('scroll', onScroll, true)
}, [handleScroll])
In Keeper, assign the scroll property to the element:
useEffect(() => {
const catheState = catheStates[cacheId]
if (catheState && catheState.doms) {
const doms = catheState.doms
doms.forEach((dom: any) => {
(divRef?.current as any)?.appendChild?.(dom)
})
// 新增
doms.forEach((dom: any) => {
if (catheState.scrolls[dom]) {
dom.scrollTop = catheState.scrolls[dom]
}
})
} else {
mount({
cacheId,
reactElement: props.children
})
}
}, [catheStates])
If you don't actively add the method of assigning scroll
, the scroll distance will not be saved, because Keeper
is new every time.
10. CacheContext inside Keeper sub-component inside KeepAliveProvider
We are rendering the component in KeepAliveProvider
, then if a Provider
is defined inside the KeepAliveProvider
KeepAliveProvider
level component, then the ---463bc8b78e1fabbc It is impossible to use Consumer
to get this value.
Here is a question, how to change the context of the component in KeepAliveProvider
to the context of the component Keeper
.
Here is the most direct way to let the user pass in Provider
and value
value.
<Keeper
cacheId="home"
context={{ Provider: Provider, value: value }}>
<Home />
</Keeper>
After we get these two values, we directly modify the structure of Keeper
in reactElement
:
mount({
cacheId,
reactElement: context ?
<context.Provider
value={context.value}>{props.children}</context.Provider> :
props.children
})
When it is detected that context
has a value, it is directly covered with a layer outside props.children
, of course there is a multi-layer Provider
the nesting problem has not been solved, because gradually Complicated, its usefulness has been declining, and then there is a new one bug
.
11. Components that need to pass values
Have you found that all the logic of the above components is directly written in the Keeper
tag, and there is no value transfer, but a more common scenario is as follows:
function Root (){
const [n, setN] = useState(1)
return
(
<>
<button onClick={()=>setN(n+1)}>n+1</button>
<Keeper>
<Home n={n} />
</Keeper>
</>
)
}
This n
is Keeper
the outer layer is passed to the Home
component, this way of writing will lead to n
but c2242a28 Home
changed Home
will not respond inside.
This bug
I found this way, when I used this plugin in a form-based page in our team's project, table
always showed empty, and input The box also cannot enter a value. After testing, it was found that the value actually changed, but it was not displayed on the component dom
.
After trying for a long time, I tried it react-activation
Unfortunately, it also has the same problem, which actually means that this bug
may not be able to solve or it is a problem with the architecture of the plugin itself.
12. Why such a strange bug scene
At that time, this bug
tortured me for a day and a half, and the parameters that were finally positioned to the outside world were no longer the parameters of the component itself. The actual rendering position of our component was the first of KeepAliveProvider
One layer, while the outer layer of ---ab8545279134efbe1f00cc7fbec1de12 Keeper
is still in the inner layer of KeepAliveProvider
, which leads to the fact that changes in these values have no effect on the component.
It can be understood as the change of these values, such as the change of n
is like the change of window.n
, react
the component will not respond to this change.
In fact, what we need to do is to change the value passed in from the outer layer, which can drive the style change of the component (gradually enter the pit!).
13. Take out props separately
I borrowed another keep-alive
component writing method on the Internet, and changed the Keeper
component to a keeper
method, this method returns a component, so you can see Receive a props
, and set the variable in the range of props
:
const Home = keeper(HomePage, { cacheId: 'home' })
function Root(){
const [n, setN] = useState(1)
return (
<>
<button onClick={()=>setN(n+1)}>n+1</button>
<Home n={n}> // 此处可以传值了
</>
)
}
The purpose of this is to allow developers to pass in the parameters that can affect the state of the component. For example, the previous one Keeper
can have multiple components in it. In this case, it is difficult to control which parameter changes will cause Which components are updated, but it is obvious in the way of components that the changes in the values received by the components props
will cause the components to be updated.
The solution I thought of is to create a new propsObj
KeepAliveProvider
which is used to specifically store the props
of each cache component. , is to separate the logic of the participating components. Many logics will monitor the changes of catheStates
and execute them, but the changes of props
do not need to trigger these.
const [propsObj, setPropsObj] = useState<any>();
return (
<CacheContext.Provider value={{ setPropsObj, propsObj }}>
{props.children}
//.... 略
KeepAliveProvider
The rendering inside needs to be changed to a form, reactElement
become a component, don't forget to change the name to uppercase.
// 旧的
// {reactElement}
// 新的
{propsObj &&
<ReactElement {...propsObj[cacheId]}></ReactElement>}
To modify Keeper
file, first change the file name to keeper
, and change the export method.
export default function (
RealComponent: React.FunctionComponent<any>, { cacheId = '' }) {
return function Keeper(props: any) {
// ... 略
Keeper
Inside mount
The use of the method is also slightly adjusted:
mount({
cacheId,
ReactElement: RealComponent
})
The key is here, we have to monitor the changes of props
in Keeper
d5e4da655c64db65f79f6e6e96e69352---, to update propsObj
:
const { propsObj, setPropsObj } = useContext(CacheContext)
useEffect(() => {
setPropsObj({
...propsObj,
[cacheId]: props
})
}, [props])
14. Cache invalidation bug
We have modified the form of the plug-in above, and found that the following scenes can be rendered normally. The props of the Home component are passed in from the outside world:
const Home = keeper(HomePage, { cacheId: 'home' })
const RootComponent: React.FC = () => {
return (
<KeepAliveProvider>
<Router>
<Routes>
<Route path={'/'} element={<Mid />} />
</Routes>
</Router>
</KeepAliveProvider>
)
}
function Mid() {
const [n, setN] = useState(1)
return (
<div>
<button onClick={() => setN(n + 1)}>n+1</button>
<Home n={n}></Home>
</div>
)
}
function HomePage(props: { n: number }) {
return <div>home {props.n}</div>
}
But at this time, if you switch pages and then return to the home
page, home
page cache will be invalid.
In fact, because we monitor the changes of props
in real time, the next re-rendering will cause props
to change, and then the value will be initialized, causing the component to return to its earlier configuration, but .... isn't this a cache failure?
Every time the component props
is reset, the relevant data of the component will be reset, try to change the home
component as follows:
function HomePage(props: { n: number }) {
const [x, setX] = useState(1)
return (
<div>
<button onClick={() => setX(x + 1)}>x + 1</button>
<div>home {props.n}</div>
<div>home: x {x}</div>
</div>
)
}
The above writing method will cause each activation of the home
component, only the value of ---3ce5ed65b7c87550a7b445994519b0ae x
will be retained, and the value of n
will be the same as the incoming one.
This change may lead to bug
, assuming that only n > 2
can make x > 3
, at this time we let n = 5
x = 4
, then switch to another page and come back, it becomes n = 1, x=4
, which violates our initial constraints, and so on. In a real and complex development environment, this phenomenon will cause All kinds of weird questions.
15. The cost of cognition
The above scenario can be controlled by the developer himself, ideally keep-alive
the plug-in is only used to deal with components that do not require external parameters and will not be affected by changes in external parameters, but this starts to be troublesome .
This kind of problem leads to an increase in the learning cost and the cost of using the plug-in for developers, and if a component does not need to pass parameters, we wrap it with keep-alive
, and then need to pass parameters later , the cost of change is troublesome to think about.
The official website of the existing components on the Internet (April 10, 2022 17:16:22) basically does not seriously tell users about the relevant issues, and often focuses on introducing "how to use" and explaining their own advantages. Cause the user to be tortured inexplicably bug
.
There is also a problem with the method of passing Provider
, it is necessary to pass the code Provider
which may not be the code of this page.
If you want to solve keep-alive
related problems, you can change the way of thinking. It is best to support a wave in the source code of react
. For example, you can specify that some components will not be destroyed. In fact, we can pay attention to it. The follow-up version of react18
, now this time period react18
has released the official version.
16. How to upgrade to react18
Method 1: create-react-app Create a new project
At this stage, you can create a react18 project directly by using the following command:
npx create-react-app my_react
The following usage --template
specifying a template doesn't work, because the template code hasn't been updated:
npx create-react-app my_react --template typescript
Here you can view the templates for all react projects that can be specified by the create-react-app project .
Method 2: Retrofit of old projects
First, directly change the version numbers of react
and react-dom
in the dependencies to "^18.0.0"
.
Both methods need to modify index.js
There will be an error message when starting the project:
Legacy index.js
New version index.js
Not much else has changed.
Seventeen, the usage of react18 Offscreen component
Offscreen
allow React
keep this state by hiding the component instead of unloading it, React
will also call the same lifecycle hook as when it is unloaded, but Preserve the status of React
component and DOM
element.
React Activatio
n also recommend that you pay attention to this attribute:
Offscreen
What is the official statement, you can see the translation in this article: React v18.0 New Features Official Documentation [Chinese and English
Test case for Offscreen
:
Unfortunately, the Offscreen
component has not been launched in the current version, and it is still in an unstable stage, but we can preview its usage through the test cases in react18
:
It is still not clear from the above writing method Offscreen
how to use it, I only know that it may appear in the form of a component, and a mode attribute needs to be passed in. More usage is expected to be officially launched as soon as possible.
end
Let us look forward to react18
to solve this problem keep-alive
this is the case this time, I hope to make progress with you.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。