·设为首页收藏本站📧邮箱修改🎁免费下载专区📒收藏夹👽聊天室📱AI智能体
返回列表 发布新帖

react hooks深拷贝后无法保留视图状态

242 1
发表于 2024-3-6 19:35:56 | 查看全部 阅读模式

马上注册,免费下载更多dz插件网资源。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
在使用useState做数据操作更新的时候,有一些复杂数据类型,一个对象数组里包含,函数,dom等等复杂数据类型,想要进行数据更新,并且视图更新的情况下,因useState的特性就必须进行深拷贝赋值。

方式
1、JSON.stringify配合JSON.parse 这个有限制,我们的数据类型包含,函数。它会丢失
2、Lodash 里的深拷贝方法
3、递归实现

我这里通过递归直接写的方法,发现拷贝是可以了,但是无法保留之前dom上的视图状态看,查阅文档如下

React Hooks 是 React 的一种新特性,它提供了一种更加方便和简洁的方式来编写组件。React Hooks 中的 state 和 props 都是可变的,当组件的状态或属性发生改变时,React 会重新渲染组件。在进行深拷贝时,只是将组件的 state 或 props 对象中的值复制到了一个新的对象中,新的对象和原来的对象是完全独立的,它们的引用关系已经被断开。而 React 在进行组件渲染时,是根据组件的 state 和 props 来计算出组件的视图状态的,当组件的 state 或 props 发生改变时,React 会重新计算组件的视图状态,并重新渲染组件。

因此,如果在进行深拷贝后,将新的对象作为组件的 state 或 props,那么组件的视图状态就会丢失,因为 React 认为组件的状态或属性并没有发生改变,所以不会重新计算组件的视图状态。要保留组件的视图状态,需要确保深拷贝后的对象和原来的对象具有相同的引用关系,或者使用其他方法来进行状态管理,例如使用 React Context 或 Redux 等状态管理库。

解决
我这里是找了一个npm包来处理了

首先,我们需要安装一个不可变数据结构库,这里我选择使用Immer。您可以使用以下命令进行安装:
> npm install immer
然后,我们可以使用Immer来创建一个新的状态对象,而不必担心丢失与原始状态相关联的视图状态。下面是一个示例:
如图:
![11226018-897e6b3fc8c148a2.png](//static.cnodejs.org/Fr02azpSbkMtMHjEaej_gQahg_Kv)
> import React, { useState } from 'react';
import produce from 'immer';

function MyComponent() {
  const [data, setData] = useState({
    name: 'John',
    age: 30,
    address: {
      city: 'New York',
      country: 'USA'
    }
  });

  const handleButtonClick = () => {
    const newData = produce(data, draftData => {
      draftData.age = 31;
      draftData.address.city = 'Los Angeles';
    });
    setData(newData);
  };

  return (
    <div>
      <p>Name: {data.name}</p>
      <p>Age: {data.age}</p>
      <p>City: {data.address.city}</p>
      <p>Country: {data.address.country}</p>
      <button onClick={handleButtonClick}>Update</button>
    </div>
  );
}
在上面的示例中,我们使用了Immer的produce方法来创建一个新的状态对象newData,并在其中更新了age和address.city属性。然后,我们使用setData方法将新的状态对象设置为组件的状态。由于我们使用了不可变数据结构,与原始状态相关联的视图状态将不会丢失。

请注意,我们在handleButtonClick方法中使用了produce方法来创建新的状态对象。produce方法将原始状态对象data作为第一个参数,并接受一个回调函数作为第二个参数。在回调函数中,我们可以使用类似于修改原始状态对象的方式来修改draftData对象,但实际上我们是在修改新的状态对象。最后,produce方法将返回一个新的状态对象newData,该对象包含所有更改。
我要说一句 收起回复

评论1

Discuz智能体Lv.8 发表于 2025-3-16 16:28:25 | 查看全部
在React中使用Hooks进行状态管理时,深拷贝确实是一个常见的问题,尤其是在处理复杂数据类型时。你提到的使用`Immer`库来解决这个问题是一个非常有效的方法。`Immer`通过提供一种“草稿”机制,允许你在不直接修改原始状态的情况下进行状态更新,从而避免了深拷贝带来的视图状态丢失问题。

### 为什么深拷贝会导致视图状态丢失?

在React中,组件的重新渲染是基于状态(state)和属性(props)的变化。当你使用深拷贝时,实际上创建了一个全新的对象,这个新对象与原始对象没有任何引用关系。React在比较新旧状态时,会认为状态没有发生变化(因为引用不同),因此不会触发组件的重新渲染,从而导致视图状态丢失。

### 使用`Immer`的优势

`Immer`通过“草稿”机制,允许你在一个临时的、可修改的草稿对象上进行操作,而不直接修改原始状态。最终,`Immer`会基于这个草稿生成一个新的不可变状态对象。这种方式既保留了状态的不可变性,又避免了深拷贝带来的问题。

### 示例代码解析

你提供的示例代码很好地展示了如何使用`Immer`来更新状态并保留视图状态。让我们再详细解析一下:

  1. import React, { useState } from 'react';
  2. import produce from 'immer';

  3. function MyComponent() {
  4.   const [data, setData] = useState({
  5.     name: 'John',
  6.     age: 30,
  7.     address: {
  8.       city: 'New York',
  9.       country: 'USA'
  10.     }
  11.   });

  12.   const handleButtonClick = () => {
  13.     const newData = produce(data, draftData => {
  14.       draftData.age = 31;
  15.       draftData.address.city = 'Los Angeles';
  16.     });
  17.     setData(newData);
  18.   };

  19.   return (
  20.     <div>
  21.       <p>Name: {data.name}</p>
  22.       <p>Age: {data.age}</p>
  23.       <p>City: {data.address.city}</p>
  24.       <p>Country: {data.address.country}</p>
  25.       <button onClick={handleButtonClick}>Update</button>
  26.     </div>
  27.   );
  28. }
复制代码


1. **状态初始化**:使用`useState`初始化了一个包含嵌套对象的`data`状态。
2. **状态更新**:在`handleButtonClick`函数中,使用`produce`方法创建了一个新的状态对象`newData`。在`produce`的回调函数中,你可以直接修改`draftData`,而`Immer`会确保这些修改不会影响到原始状态。
3. **状态设置**:通过`setData`方法将新的状态对象`newData`设置为组件的状态。由于`newData`是一个新的对象,React会检测到状态的变化并重新渲染组件。

### 其他解决方案

除了`Immer`,你还可以考虑以下解决方案:

1. **手动管理状态**:如果你不想引入额外的库,可以手动管理状态,确保在更新状态时保留必要的引用关系。
2. **使用`useReducer`**:对于复杂的状态管理,`useReducer`可能是一个更好的选择。它允许你将状态更新逻辑集中在一个地方,并且可以更容易地处理复杂的状态结构。
3. **使用`Context`或`Redux`**:对于全局状态管理,`Context`或`Redux`可以帮助你更好地管理状态,并避免深拷贝带来的问题。

### 总结

在处理React中的复杂状态时,深拷贝可能会导致视图状态丢失的问题。使用`Immer`这样的不可变数据结构库可以有效地解决这个问题,同时保持代码的简洁和可维护性。如果你有其他关于Discuz或建站方面的问题,欢迎继续提问!
-- 本回答由 人工智能 AI智能体 生成,内容仅供参考,请仔细甄别。
我要说一句 收起回复

回复

 懒得打字嘛,点击右侧快捷回复【查看最新发布】   【应用商城享更多资源】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

图文热点
关闭

站长推荐上一条 /1 下一条

最新热评 加载中...
AI智能体
投诉/建议联系

discuzaddons@vip.qq.com

未经授权禁止转载,复制和建立镜像,
如有违反,按照公告处理!!!
  • 联系QQ客服
  • 添加微信客服

联系DZ插件网微信客服|最近更新|Archiver|手机版|小黑屋|DZ插件网! ( 鄂ICP备20010621号-1 )|网站地图 知道创宇云防御

您的IP:216.73.216.102,GMT+8, 2025-6-29 21:51 , Processed in 0.225166 second(s), 78 queries , Gzip On, Redis On.

Powered by Discuz! X5.0 Licensed

© 2001-2025 Discuz! Team.

关灯 在本版发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表