前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >immutability-helper实践与优化

immutability-helper实践与优化

作者头像
前端进击者
发布2021-11-30 14:50:56
8860
发布2021-11-30 14:50:56
举报
文章被收录于专栏:前端有的玩前端有的玩

react hook使用的是Object.is来进行的比较,这个比较是一个浅比较。这也意味着对于一个对象,直接修改对象里面的值,是不会触发组件的重渲染的。但是如果我们使用深clone,又会引起性能问题,那怎么办呢?

从一个🌰来看问题

代码语言:javascript
复制
import React, { useEffect, useMemo, useState } from 'react';
import update from 'immutability-helper';
import { Button } from 'antd';
import Child from './Child';
import { cloneDeep } from 'lodash';

const Test = () => {
  const [data, setData] = useState({
    info: {
      name: 'tom',
      age: 12,
    },
    score: {
      exam1: [99, 98, 89],
      exam2: [78, 85, 33],
    },
  });

  function handleClick() {
    // TODO: 点击按钮,更新第一个考试的英语成绩
  }

  const examStr = useMemo(() => {
    const exam1 = data.score.exam1;
    return (
      <div>
        <p>语文: {exam1[0]}</p>
        <p>数学: {exam1[1]}</p>
        <p>英语: {exam1[2]}</p>
      </div>
    );
  }, [data.score.exam1]);

  return (
    <div>
      <Button onClick={handleClick}>更新数据</Button>
      <div>{examStr}</div>
      <Child child={data.info}></Child>
    </div>
  );
};

export default Test;

来看上面的代码,我们需要点击按钮的时候更新exam1数组的第三项数据,这时候应该如何实现呢?

实现方式一(失败)

代码语言:javascript
复制
data.score.exam1.push(100);
setData(data);

实现方式二(失败)

代码语言:javascript
复制
data.score.exam1[2] = Math.random() * 100;
setData({
   ...data,
});

实现方式三(成功,但不推荐)

代码语言:javascript
复制
import { cloneDeep } from 'lodash';

data.score.exam1[2] = Math.random() * 100;
setData(cloneDeep(data));

我们通过对data进行深复制,返回一个新的对象,通过这种方式是可以实现数据更新成功,但是也会引发一个新的问题,就是本来我们只需要更新exam1,但是缺导致info也变成了一个新的对象,引起Child组件的重新渲染。

实现方式四(成功)

代码语言:javascript
复制
data.score.exam1[2] = Math.random() * 100;
 setData({
   ...data,
   score: {
     ...data.score,
     exam1: [...data.score.exam1],
   },
 });

总结

为了更新一个数组的某一项的值,我们尝试了上面的四种方式,其中有两种是成功的,但是只有最后一种方式是比较好的,使用最后一种在更新数据的同时,尽可能的降低了对其他数据引用的破坏,但是我们示例数据只有三层,在代码中我们使用了三次...扩展运算符,如果层级更深,这样更新就会变得特别麻烦了。

使用 immutability-helper

如何能达到即优雅又高效的去变更数据,我这里使用到了immutability-helper

代码语言:javascript
复制
setData((data) => {
  return update(data, {
    score: {
      exam1: {
        2: {
          $set: Math.random() * 10,
        },
      },
    },
  });
});

使用immutability-helper可以按需去调整数据,而且它只会去调整需要修改的数据,复用未修改的数据,和实现方式四的效果是一致的。

API介绍

$push 给数据末尾添加数据
代码语言:javascript
复制
const [data, setData] = useState<any[]>([1,2]);

setData((data) => {
  // data 值为 [1,2,3,4]
  return update(data, {
    // $push的参数必须是一个数组
    $push: [3, 4],
  });
});
$unshift 给数组开头添加数据
代码语言:javascript
复制
const [data, setData] = useState<any[]>([3,4]);

setData((data) => {
  // data 值为 [1,2,3,4]
  return update(data, {
    // $unshift的参数必须是一个数组
    $unshift: [1,2],
  });
});
$splice 修改数组数据,包括添加,删除数据
代码语言:javascript
复制
const [data, setData] = useState<any[]>([3,4]);

setData((data) => {
  // data 值为 [3,6,5]
  return update(data, {
    // $splice的参数必须是一个二维数组
    $splice: [[1,1,6,5]],
  });
});
$set 给对象的某个元素赋值
代码语言:javascript
复制
  const [data, setData] = useState<any[]>([
    {
      user: [
        {
          name: 'superfeng',
        },
      ],
    },
  ]);

setData((data) => {
   //修改name的值
      return update(data, {
        0: {
          user: {
            0: {
              name: {
                $set: '冯超',
              },
            },
          },
        },
      });
    });
$unset 从对象中删除元素
代码语言:javascript
复制
const [data, setData] = useState<any[]>([
    {
      user: [
        {
          name: 'superfeng',
          sex: '男',
        },
      ],
    },
  ]);

// 将sex从对象中删除
setData((data) => {
      return update(data, {
        0: {
          user: {
            0: {
              $unset: ['sex'],
            },
          },
        },
      });
    });
$merge 合并对象
代码语言:javascript
复制
  const [data, setData] = useState<any[]>([
    {
      user: [
        {
          name: 'superfeng',
          sex: '男',
        },
      ],
    },
  ]);

setData((data) => {
  return update(data, {
    0: {
      user: {
        0: {
          $merge: {
            age: 16,
          },
        },
      },
    },
  });
});
$apply 将当前值传递给函数,并用新的返回值更新它
代码语言:javascript
复制
const [data, setData] = useState<any[]>([
    {
      user: [
        {
          name: 'superfeng',
          sex: '男',
        },
      ],
    },
  ]);

setData((data) => {
  return update(data, {
    0: {
      user: {
        0: {
          $apply: (user: any) => {
            return Object.assign({}, user, { age: 15 });
          },
        },
      },
    },
  });
});

简化 immutabilty-helper

虽然使用immutabilty-helper可以按需更新数据,但是对于层级比较多的数据来说,书写也是很麻烦的,有没有更好的方式去更新数据呢?我封装了一个这样的函数

代码语言:javascript
复制
const toImmutability = (path: string, value: any) => {
  const arrReg = /\[(\d+)\]/;
  const keys = path.split('.').filter((item) => item);
  const result = {} as any;
  let current = result;
  const len = keys.length;
  keys.forEach((key: string, index: number) => {
    const matches = key.match(arrReg);
    if (matches && matches.length) {
      const idx = parseInt(matches[1]);
      current[idx] = index === len - 1 ? value : {};
      current = current[idx];
    } else {
      current[key] = index === len - 1 ? value : {};
      current = current[key];
    }
  });
  return result;
};

使用这个函数,我们就可以通过下面的方式来更新数据了

代码语言:javascript
复制
setData((data) => {
  return update(
    data,
    toImmutability('[0].user.[0].name', {
      $set: '冯超',
    })
  );
});

toImmutability函数的第一个参数是一个字符串,这个字符串是由需要更新数据的路径拼接而成的,数组使用[index]来表示,然后在函数内解析这个字符串,构建需要更新的对象就可以了

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端有的玩 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从一个🌰来看问题
    • 实现方式一(失败)
      • 实现方式二(失败)
        • 实现方式三(成功,但不推荐)
          • 实现方式四(成功)
            • 总结
            • 使用 immutability-helper
              • API介绍
                • $push 给数据末尾添加数据
                • $unshift 给数组开头添加数据
                • $splice 修改数组数据,包括添加,删除数据
                • $set 给对象的某个元素赋值
                • $unset 从对象中删除元素
                • $merge 合并对象
                • $apply 将当前值传递给函数,并用新的返回值更新它
            • 简化 immutabilty-helper
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档