为 useState() 钩子字符串化对象以避免重新渲染是一种好习惯吗

Posted

技术标签:

【中文标题】为 useState() 钩子字符串化对象以避免重新渲染是一种好习惯吗【英文标题】:Is it good Practice to Stringify Object for useState() Hook To Avoid Re-Rendering 【发布时间】:2020-07-03 01:04:43 【问题描述】:

根据this 和that 的问题,spread operator 似乎用于更新在useState 挂钩中管理的对象。

我创建了一个超级简单的例子,发现即使对象的内容没有改变,也会触发重新渲染(很明显,因为对象改变了):

import React from "react";

function useFriendStatus() 
  const [person, setPersonProps] = React.useState(name:'Mark',age:23);

  React.useEffect(() => 
    console.log("rerender");
    const interval = setInterval(() => 
      setPersonProps(...person);   //simply set the object again -> no content changed
      console.log('update');
    , 1000);
    return () => clearInterval(interval);
  , [person]);


  return person;


export default function App() 
  const person = useFriendStatus();

  return <div className="App">Hello World: "" + person.name</div>;

在这里您可以看到我的分析器的屏幕截图,显示似乎触发了重新渲染(即使显示的名称没有改变):

我想知道这是否是一种“好习惯”,因为一切似乎都被重新渲染了。有时,您从 API 获得深度嵌套的对象并将它们分解为超级简单的非对象 userState 挂钩是不可能的。

字符串化不是更好吗?

import React from "react";


function useFriendStatus() 
  const [person, setPersonProps] = React.useState(JSON.stringify(name:'Mark',age:23));

  React.useEffect(() => 
    console.log("rerender");
    const interval = setInterval(() => 
      const personCopy=JSON.parse(person);
      setPersonProps(JSON.stringify(...personCopy));
      console.log('update');
    , 1000);
    return () => clearInterval(interval);
  , [person]);


  return person;


export default function App() 
  const person = JSON.parse(useFriendStatus());

  return <div className="App">Hello World: "" + person.name</div>;

在实践中你是如何处理的?

【问题讨论】:

【参考方案1】:

我创建了一个超级简单的例子,发现即使对象的内容没有改变,也会触发重新渲染(很明显,因为对象改变了)

它与对象的“内容”无关,您的组件会重新渲染,因为您创建了对象的浅表副本...person(更改其引用)。

在渲染阶段,React 与之前的状态进行浅比较,以确定是否会发生渲染,而在 javascript 中, === 始终为 false。

扩展运算符似乎用于更新在 useState 挂钩中管理的对象。

由于状态应该被视为immutable,因此通常使用扩展运算符进行浅拷贝。

"只有在对象的 name 属性发生变化时才应该重新渲染"

在调用setState之前添加一个条件是很常见的:

React.useEffect(() => 
  const newPerson =  ...person ; // fetch from some source
  // or check if person.name !== newPerson.name
  if (!isEqual(person, newPerson)) 
    setPerson(newPerson);
  
, [person]);

【讨论】:

我很清楚“为什么”会执行重新渲染,但我想知道“如何”阻止它...... @user3579222 React.memo 您的示例的预期行为是什么?使用 stringify 比较字符串与比较对象不同 只有在对象的 name 属性发生变化时才应该重新渲染 我正在更新答案

以上是关于为 useState() 钩子字符串化对象以避免重新渲染是一种好习惯吗的主要内容,如果未能解决你的问题,请参考以下文章

使用 prevState 和 useState 钩子更新对象内部数组中的对象

React Native:如何从循环中获取值到钩子 useState

使用钩子从数组中删除对象(useState)

反应钩子useState()的真正奇怪的行为

反应钩子:useState/context;无法读取未定义的属性“头像”/如何更新嵌套对象

使用 React useState() 钩子更新和合并状态对象