React hooks:直接改变嵌套对象有什么缺点?

lis*_*nge 3 javascript typescript reactjs react-hooks

import { useState } from "react";

function App() {
  const [form, setForm] = useState({
    usename: ``,
    password: ``,
    avatar: { id: 0, url: ``, deep: { id: 0 } },
  });
  return (
    <div>
      <h1>ID:{form.avatar.deep.id}</h1>
      <h2
        onClick={() => {
          form.avatar.deep.id += 1;
          setForm({ ...form });
        }}
      >
        Change-ID
      </h2>
    </div>
  );
}

export default App;
Run Code Online (Sandbox Code Playgroud)

代码沙盒在线运行

我学习了 reat hooks,人们告诉我不要更改嵌套对象。

我们应该保持原来的状态不变或者使用https://github.com/immerjs/immer

但是上面的代码也可以更新UI,为什么呢?

有什么form.avatar.deep.id+=1;setForm({ ...form });缺点?

直接改变嵌套对象的属性有什么缺点?

Nic*_*wer 5

什么是 form.avatar.deep.id+=1;setForm({ ...form }); 缺点?

React 的设计考虑到了不可变状态。这允许进行非常便宜的===比较来判断事情是否发生了变化。您已经复制了form,这足以让此示例代码正常工作。新旧表单是不同的对象,因此当您设置状态时组件会重新呈现。

如果某段代码关心表单的一部分而不是整个表单,就会出现问题。如果一个组件想要检查是否form.avatar改变了,它会看到没有,它没有改变,但事实上它已经改变了!

例如,假设我们将一个Avatar组件拆分如下:

import { memo } from 'react';

function App() {
  const [form, setForm] = useState({
    usename: ``,
    password: ``,
    avatar: { id: 0, url: ``, deep: { id: 0 } },
  });

  return (
    <div>
      <Avatar avatar={form.avatar} />
      <h2
        onClick={() => {
          form.avatar.deep.id += 1;
          setForm({ ...form });
        }}
      >
        Change-ID
      </h2>
    </div>
  );
}

const Avatar = memo(function ({ avatar }) {
  useEffect(() => {
    // Supposed to do some stuff, if avatar has changed
  }, [avatar]);

  return (
    <h1>ID:{avatar.deep.id}</h1>
  )
})
Run Code Online (Sandbox Code Playgroud)

在上面的代码中, 和memouseEffect被破坏了。memo如果 props 改变了,应该渲染组件,如果没有改变则跳过渲染。但它看起来avatar永远不会改变,因此它永远不会重新渲染。同样,如果组件确实渲染,则使用效果将永远认为avatar没有改变,并且不会重新运行效果。