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.

  1. 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).
  2. 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