Gahing's blog Gahing's blog
首页
知识体系
  • 前端基础
  • 应用框架
  • 工程能力
  • 应用基础
  • 专业领域
  • 业务场景
  • 前端晋升 (opens new window)
  • Git
  • 网络基础
  • 算法
  • 数据结构
  • 编程范式
  • 编解码
  • Linux
  • AIGC
  • 其他领域

    • 客户端
    • 服务端
    • 产品设计
软素质
  • 面试经验
  • 人生总结
  • 个人简历
  • 知识卡片
  • 灵感记录
  • 实用技巧
  • 知识科普
  • 友情链接
  • 美食推荐 (opens new window)
  • 收藏夹

    • 优质前端信息源 (opens new window)
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Gahing / francecil

To be best
首页
知识体系
  • 前端基础
  • 应用框架
  • 工程能力
  • 应用基础
  • 专业领域
  • 业务场景
  • 前端晋升 (opens new window)
  • Git
  • 网络基础
  • 算法
  • 数据结构
  • 编程范式
  • 编解码
  • Linux
  • AIGC
  • 其他领域

    • 客户端
    • 服务端
    • 产品设计
软素质
  • 面试经验
  • 人生总结
  • 个人简历
  • 知识卡片
  • 灵感记录
  • 实用技巧
  • 知识科普
  • 友情链接
  • 美食推荐 (opens new window)
  • 收藏夹

    • 优质前端信息源 (opens new window)
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 前端基础

  • 应用框架

    • UI 框架

      • Angular

      • React

        • React Hooks 实现原理
        • React 组件销毁需要注意的异步更新问题
        • React项目结构规范
        • React Fiber 实现原理
        • React Fiber 本质
        • React「 Refs 转发 」初探
        • React之低版本chrome setState loading2次状态出现的bug
        • react学习
        • 使用 immer 修改复杂对象
          • 使用 immer 修改复杂对象
          • data.a 引用不变
          • 逐层解构
          • 完全深拷贝
          • 按需拷贝
          • immer 的实现原理 (wip)
          • 拓展阅读
        • input 原生控件,React 是如何实现受控输入的?
        • useEffect 如何处理 async
        • 用组件替换文本中emoji字符
        • 谈谈 React 组件设计原则
      • Solid

      • Svelte

      • Vue

      • 框架本质

    • 开发框架

    • 组件库

  • 工程能力

  • 应用基础

  • 专业领域

  • 业务场景

  • 大前端
  • 应用框架
  • UI 框架
  • React
gahing
2022-08-23
目录

使用 immer 修改复杂对象草稿

# 使用 immer 修改复杂对象

export default function App() {
  const [data, setData] = useState({
    a: {
      b: {
          c: 1
      },
      d: {}
    },
  });
  useEffect(() => {
    console.log('a 变更了', data.a);
  }, [data.a]);
  const handleUpdate = () => {
    const random = Math.round(Math.random() * 100);
    // 更新 data.a.b.c 的值
  };
  console.log("rerender");
  return (
    <div>
      <div>{data.a.b.c}</div>
      <button onClick={handleUpdate}>update</button>
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

考虑这个场景,应该怎么更新 data.a.b.c ?

有人说直接改,再最外层解构,得到一个新的 data

data.a.b.c = random
setData({
  ...data
})
1
2
3
4

看起来每个更新浏览器也都变更了,貌似没问题?

# data.a 引用不变

但是这里发现没有,useEffect 里 data.a 是一直不变更的

如果此时有个组件依赖了 a 并做了 memo

import { memo, useEffect, useState } from "react";
import "./styles.css";

const Child = memo(({ a }) => {
  return <div>{a.b.c}</div>;
});

export default function App() {
  const [data, setData] = useState({
    a: {
      b: {
        c: 1
      },
      d: {}
    }
  });
  useEffect(() => {
    console.log("a 变更了", data.a);
  }, [data.a]);
  const handleUpdate = () => {
    const random = Math.round(Math.random() * 100);
    // 更新 data.a.b.c 的值
    data.a.b.c = random;
    setData({
      ...data
    });
  };
  console.log("rerender");
  return (
    <div>
      <div>{data.a.b.c}</div>
      <button onClick={handleUpdate}>update</button>
      <Child a={data.a} />
    </div>
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

那么此时子组件是始终不会变化的,因为 data.a 的引用一直不变

绝大多数场景下,一个对象的属性发生了改变,那么就应该生成一个新的对象,而是否重渲染是交由外部来判断的

那么我们应该怎么处理呢?

# 逐层解构

修改更新的代码:

setData({
  ...data,
  a: {
    ...data.a,
    b: {
      ...data.a.b,
      c: random
    }
  }
})
1
2
3
4
5
6
7
8
9
10

结果符合预期

但是发现没有,这个处理非常麻烦。。。有什么解决方案么?

# 完全深拷贝

使用 loadsh 的 deepClone 功能,将 data 完全深拷贝一份再修改

setData((data) => {
  const newData = deepClone(data)
  newData.a.b.c = random;
  return newData
});
1
2
3
4
5

结果符合预期,但是这个完全深拷贝性能比较低,因为不是所有子对象都需要深拷贝!

# 按需拷贝

那么怎么样避免深拷贝所有属性,而只针对目标属性和子对象

可以使用 immer 这个工具库

上面的代码,我们只要按如下方法修改即可

import { produce } from 'immer'

setData((data) =>
  produce(data, (draft) => {
    draft.a.b.c = random;
  })
);
1
2
3
4
5
6
7

结果符合预期

# immer 的实现原理 (wip)

  • 将待修改对象 state 做了 proxy 得到了 draft
  • 处理 draft 变更
    • 一个赋值操作其实是由一个 get 和 set 组成

对 draft proxy 的改变进行收集,如果

# 拓展阅读

https://zhuanlan.zhihu.com/p/146773995

编辑 (opens new window)
上次更新: 2024/09/01, 23:56:56
react学习
input 原生控件,React 是如何实现受控输入的?

← react学习 input 原生控件,React 是如何实现受控输入的?→

最近更新
01
浅谈代码质量与量化指标
08-27
02
快速理解 JS 装饰器
08-26
03
Vue 项目中的 data-v-xxx 是怎么生成的
09-19
更多文章>
Theme by Vdoing | Copyright © 2016-2024 Gahing | 闽ICP备19024221号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式