immer的使用

227 篇文章 1 订阅
30 篇文章 0 订阅

为什么 React 需要 Immutable Data

简单来说为了让 React 精准地重渲染 UI。我们知道,在 React 中,UI 是 state 的投影,state 的变更会引发 UI 的重新渲染。React 使用 Virtual DOM 来解决 UI 更新的问题——它会将新旧两棵 Virtual DOM 树进行比较,如果两者存在差异,则它会将这些差异来更新在真实的 DOM 上。
调用setState时,React 会以 shallowMerge(浅层合并) 的方式将我们传入的对象与旧的 state 进行合并。shallowMerge 只会合并新旧 state 对象中第一层的内容,如果 state 中对象的引用未变,那么 React 认为这个对象前后没有发生变化。所以如果我们以 mutable 的方式更改了 state 中的某个对象, React 会认为该对象并没有更新,那么相对应的 UI 就不会被重渲染。而以 Immutable 的方式更新 state 就不会出现这个问题。

import React, { useState } from "react";


export default function App() {
  const [list, setList] = useState([1, 2, 3]);

  const addMutable = () => {
    list.push("新数据");
    setList(list);
  };

  const addImmutable = () => {
    setList([...list, "新数据"]);
  };

  return (
    <div className="App">
      <button onClick={addMutable}>已可变的方式添加</button>
      <button onClick={addImmutable}>已不可变的方式添加</button>
      {list.map((item, index) => (<li key={index}>{item}</li>))}
    </div>
  );
}

redux / flux 要求采用返回新对象的形式,来触发数据更新、re-render,一般推荐的做法就是采用对象解构的方式。如果 state 对象巨大(注意:对象巨大),在结构、拷贝 state 的过程中,耗时会较长。

return {
  ...state,
  settings: {
    ...state.settings,
    profile:{
      ...state.settings.profile,
      darkmode: true,
    }
  }
}

Immer:

**开源库实现思路:**原始对象先做了一层 Proxy 代理,得到 draftState 传递给 function。function(带副作用) 直接更改 draftState,最后 produce 返回新的对象

npm install immer
import React, { useState } from "react";
import produce from "immer";


export default function App() {
  const [list, setList] = useState([1, 2, 3]);

  const addMutable = () => {
    list.push("新数据");
    setList(list);
  };

  const addImmutable = () => {
    /**
     * 第一个参数是要代理的数据
     * 第二个参数是一个函数
     */
    const newVal = produce(list, draft => {
      /**
       * draft 相当于 list
       * 在这个方法里面,可以直接修改draft,注意draft也只能在这个方法里面修改
       * 不需要返回值,immer内部已经帮我处理好了
       */
      draft.push('新数据')
    })
    console.log(newVal)
    setList(newVal);
  };

  return (
    <div className="App">
      <button onClick={addMutable}>已可变的方式添加</button>
      <button onClick={addImmutable}>已不可变的方式添加</button>
      {list.map((item, index) => (<li key={index}>{item}</li>))}
    </div>
  );
}

让我们看一个示例,将 Immer 与 React 结合使用。

import produce from "immer";

this.state={
    id: 14,
    email: "stewie@familyguy.com",
    profile: {
      name: "Stewie Griffin",
      bio: "You know, the... the novel you've been working on",
      age:1
    }
}

changeBioAge = () => {
    this.setState(prevState => ({
        profile: {
            ...prevState.profile,
            age: prevState.profile.age + 1
        }
    }))
}

可以通过更改如下所示的状态来重构这段代码。

changeBioAge = () => {
    this.setState(
        produce(draft => {
            draft.profile.age += 1
        })
    )
}


结合 Hooks 使用

Immer 的另一个重要特性是它支持 React Hooks。Immer 使用一个名为 use-immer 的附加库来实现此功能。让我们来看一个示例,以便更好地理解。

const [state, setState] = useState({
    id: 14,
    email: "stewie@familyguy.com",
    profile: {
      name: "Stewie Griffin",
      bio: "You know, the... the novel you've been working on",
      age:1
    }
  });

function changeBio(newBio) {
    setState(current => ({
      ...current,
      profile: {
        ...current.profile,
        bio: newBio
      }
    }));
  }

通过将 useState 替换为 useImmer Hook,我们可以进一步简化 Hooks 示例,还可以通过更改组件状态来更新 React 组件。

import { useImmer } from 'use-immer';

const [state, setState] = useImmer({
    id: 14,
    email: "stewie@familyguy.com",
    profile: {
      name: "Stewie Griffin",
      bio: "You know, the... the novel you've been working on",
      age:1
    }
 });

function changeBio(newBio) {
   setState(draft => {
      draft.profile.bio = newBio;
    });
  }

注:这里还有一点,useState是全覆盖,setState是合并

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Immer 是一个用于处理不可变数据的库,它通过提供一种简洁的方式来创建和修改不可变数据,同时避免了不必要的数据复制。Immer 的原理主要基于结构共享(structural sharing)和代理(proxy)。 具体来说,Immer 使用了代理对象(Proxy)来拦截对原始数据的读取和写入操作。当我们对原始数据进行修改时,Immer 会创建一个代理对象,这个代理对象记录了对数据的修改操作。当我们读取数据时,Immer 会根据代理对象的记录来提供对应的数据视图,而不是直接访问原始数据。 这种方式带来了两个优势: 1. 结构共享:Immer 使用结构共享来减少不必要的数据复制。当我们对数据进行修改时,Immer 会将原始数据复制一份,并基于修改操作创建一个新的代理对象。新的代理对象只会包含被修改的部分,其他部分仍然共享原始数据。这样可以减少内存占用和复制操作的开销。 2. 懒执行:Immer 采用了惰性执行(lazy execution)的策略。即只有在实际读取修改后的数据时,Immer 才会将修改操作应用到原始数据上。这样可以避免了对每个修改操作都进行立即复制和更新,提高了性能。 由于使用了结构共享和懒执行的策略,Immer 在处理大型数据结构时能够提供更好的性能。它避免了不必要的数据复制和更新,减少了内存占用和运行时间。同时,Immer 的 API 设计也非常简洁易用,使得开发者可以更方便地处理不可变数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值