使用 React Hooks 更改数组中的对象 属性 不会引发重新渲染

Changing object property in an array with React Hooks does not evoke a re-render

当我使用 React 钩子更改数组中对象的 属性 时,组件不会重新呈现。

我咨询了我的首席技术人员,他解释说 React 搜索 "breadth" 而不是 "depth,",这意味着 React 可以看到对象,但看不到对象属性的更改。

const [array, setArray] = useState([
        {'id': 0, text: '0'},
        {'id': 1, text: '1'},
        {'id': 2, text: '2'}
    ]);

我希望组件在状态更改时重新呈现,但行为就像对象数组根本没有更改一样。

如果我将数组副本创建为 newArray 并更改 newArray[2].text = '3' 和 setArray(newArray),React 认为数组没有更改。因此,组件不会重新加载。

作为解决方法,我使用

const [trigger, setTrigger] = useState(false);
setTrigger(!trigger); 

强制重新加载。然而,这是一种重新加载组件的 hacky 方式,我想要一种更 React 的方式来完成它。

提前谢谢!

我认为问题出在您复制数组的方式上。 js 中的数组是 reference type 所以你不能只这样做:

const arr = [1,2,4]
const copy = arr

因为您只是更改指针而不是引用本身。试试这个:

const copy = [...arr]

现在您正在创建一个新数组,每个 arr 个元素的 spread 个。为此,请使用 useState

const Comp = () =>{
    const [arr, setArr] = useState([1,2,3,4])

    return <button onClick={() => setArr([...arr, 5])}>Change</button>
}

您的首席技术人员 "breath over depth" 的意思是正确的。

React 仅监视 "reference" 更改。当您更改对象的 属性 时,引用是相同的。

把它想象成当你这样做的时候,你仍然可以改变a.value,即使它被声明为常量。

const a = {value: 'abc'}
a.value = 'xxx'
console.log(a.value) // prints 'xxx'

但你做不到

const a = {value: 'abc'}
a = {something: 'bad'}
 not allowed.

这同样适用于对象数组(或普通的旧数组)。 它是引用类型,因此更改数组元素的 属性 不会更改 newArray.

的引用
newArray[2].text = '3' and setArray(newArray)

newArray 仍然指向同一个地址 space.

因此,当您使用扩展语法或 Array.from 创建一个全新的数组时,您会创建一个全新的引用(副本)并设置一个新值,最后设置状态。

const [array, setArray] = useState([
        {'id': 0, text: '0'},
        {'id': 1, text: '1'},
        {'id': 2, text: '2'}
    ]);

// ... somewhere else.
const copy = [...array, {'id': 2, ]
// or -> const copy = Array.from(array)
copy[2].text = '3' 
setArray(copy)

上面的代码创建了一个新的 copy of array 状态。使用更新值设置该副本将触发重新渲染。 (但要注意,它会创建 "shallow copy",而不是 "deep copy")

由于您的数据结构很复杂,您不妨使用 ImmerJS,这样您就可以像编写当前代码一样更改引用。