So let’s go to the code
Child.jsx
import React from "react";
interface itemProps {
add: Function,
val: number
}
function areEqual(prevProps: itemProps, nextProps: itemProps) {
return Object.is(prevProps.val, nextProps.val);
}
function Child(props: itemProps) {
const {add, val} = props;
console.log("render Child");
return (
<div onClick={() => add()}>
{val}
</div>
)
}
export default React.memo(Child, areEqual);
Copy the code
Father.jsx
import React, {useState} from 'react'; import Child from "./Child"; export interface Box { val: number, } const cartData = Array.from({length: 5}, (v, val) => val); function Index() { const [counts, setCounts] = useState<number[]>(cartData); Const add = function (index: number) {// Const target = counts. Slice (0); target[index] = target[index] + 1; setCounts(target); }; console.log("render Test.js"); return ( <div className="Test"> { counts.map((val, index) => ( <Child key={index} val={val} add={() => add(index)}/> )) } </div> ); } export default Index;Copy the code
Analysis of the code
The scenario is simple. There are multiple Child components inside the Father component. Click on one of the Child components and the corresponding array element is +1.
The react. memo() is used here, just like PureComponent, to determine if the component needs to be updated.
In this scenario, when the counts of the Father component changes, the Child component will be redrawn first, and then the Child component will be redrawn only after the memo wraps the Child, and only after the val changes. The remaining components will return the last component rendering result.
Now look at the Add method
Const add = function (index: number) {// Const target = counts. Slice (0); target[index] = target[index] + 1; setCounts(target); };Copy the code
When we click on the index Child, the counts array changes, causing the Father component to be redrawn, and the Index Child component to be redrawn as well.
To see the results
The result we want
Real-world results
We can see that the values do change when we click on one of the indices, but when we click on the other indices, the counts data looks like it’s going back to its original state.
The only place the Child component is associated with counts is the Add method. Let’s look at what the Add method does.
First copy counts and assign it to a constant. Then find the index of counts with the value +1. Finally call setCounts to change counts.
Logically there is no problem.
React Hook is actually a closure. Every render state is the latest. This is probably caused by dirty data.
The index Child component is rerendered, the component closure executes, and returns the current state of the Child component.
- For the remaining components that have not been updated, the last render result is returned directly (including all variables/constants, methods, and the final render result inside the functional component).
- The add method internally operates on the value of COUNTS, not the address.
So we’re thinking, this is reading dirty data.
The solution
Solution: Address reference instead of value reference
const add = function (index: number) { setCounts(val => { return val.map((item, i) => { if (i === 0) { item++; } return item; })}); };Copy the code