reactjs 直接修改React组件中的全局变量会导致意外的渲染结果

r1wp621o  于 2023-04-05  发布在  React
关注(0)|答案(2)|浏览(233)

我目前正在学习react,无意中发现了他们的“教程”部分中的一个部分,名为“描述UI”。我无法理解一个名为“保持组件纯净”的小节。我不明白的是:
给定以下代码:

let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

为什么输出看起来像这样:客人茶杯#2客人茶杯#4客人茶杯#6
而不是像这样客人用茶杯#1客人用茶杯#2客人用茶杯#3
我想自己弄明白,但我做不到。

ebdffaop

ebdffaop1#

这可能是因为,在开发模式下,React会在挂载时渲染所有内容两次。你可以通过在Cup中添加以下内容来证明这一点:

useEffect(() => {
  console.log('cup');
}, []);

即使useEffect没有依赖项并且应该只运行一次,您也会看到每个Cup打印两次“cup”。
考虑到这一点,每个Cup组件被渲染两次。您有三个,因此最终渲染显示每个组件第二次渲染后的结果,设置为2,4和6。
然而,像这样将状态保存在全局变量中并不是正确的做法,并且会导致意外的行为。正如您提到的,Cup应该是一个“纯”组件,它对全局变量guest的依赖性是违反直觉的。
一个更好的方法是告诉Cup接受一些输入,并始终显示该输入。这被称为propsproperties的缩写),并允许您纯粹基于状态渲染cup,这在其他地方进行管理。一个服务建议:

function Cup({ guest }) {
  // we destructure the guest from the props
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

这样就可以在TesSet中保存一个guest的状态,然后循环遍历它。一个更高级的例子:

function TeaSet() {
  const [guests, setGuests] = useState([1, 2, 3]);
  return (
    <>
      {guests.map(guest => <Cup key={guest} guest={guest} />)}
    </>
  );
}

这允许您从TeaSet中操作状态,而不会影响现有客户机的单个cup,也不依赖于任何全局变量,而全局变量可能会以意外的方式进行处理。

ruarlubt

ruarlubt2#

在开发模式下,React渲染你的组件两次,这对调试很有用(更容易找到不需要的行为,最明显的用例可能是useEffect())。
我怀疑你的index.js中有一个<React.StrictMode/>元素,这是导致这种行为的组件,但我建议不要删除它。
如果渲染是一个步骤,那么所有的计算都必须与步骤相关,我绝对建议不要在你的组件中使用外部变量,正确的方法是让另一个组件负责渲染<Cup/>并将值作为prop传递。

相关问题