一文详解React渲染优化之useImmer
短信预约 -IT技能 免费直播动态提醒
从一个例子开始
import { FC, useState, useEffect } from 'react';
const App: FC = () => {
const [list, setList] = useState({ a: 1 });
useEffect(() => {
setList({ a: 1 });
}, []);
console.log('测试');
return (
<>
<h1>Hello Web3 React {list.a}</h1>
</>
);
};
export default App;
- 看控制台输出结果
- 因为list是引用对象,虽然内部的值相同,但引用地址变化了,因此react认为state发生了变化,因此触发了渲染,这也联想到如果我们传递props时为对象或数组时,会造成我们非期望渲染的缘由,那我们该如何解决这个问题呢?
- 在此之前,先介绍一个react渲染检查工具 why-did-you-render
渲染检查工具
why-did-you-render, 可以在开发时,帮你检测无意义的渲染
- 安装
yarn add @welldone-software/why-did-you-render -D
- class="lazy" data-src下创建 wdyr.tsx
/// <reference types="@welldone-software/why-did-you-render" />
import React from 'react';
if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line
const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, {
onlyLogs: true,
titleColor: 'green',
diffNameColor: 'darkturquoise',
trackHooks: true,
trackAllPureComponents: true,
});
}
- 在入口文件 index.tsx 引入
- 4.上面的例子,输出结果,显示无意义渲染
要解决这个问题,需要我们使用到Immer
Immer 与 UseImmer
- 不可变数据:不可变数据的特点就是产出地址不同的对象引用,同时尽可能保留没必要更新的属性或者子属性,这样在你的组件树中如果有数据的其他属性或者子熟悉的依赖存在的话,可以避免一些rerender
- immutable:不改变原数据,将变化的部分与不变的部分组成一个新的数据对象**
- Immer:简化了不可变数据结构的处理,与immutable代码简单,去掉树的压缩,使用proxy代理,组合新的树
- immer 可以在需要使用不可变数据结构的任何上下文中使用,例如:React state、Redux等
- 不可变的数据结构允许(高效)的变化检测:如果对象的引用没有改变,那么对象本身也没有改变。此外,它使克隆对象相对便宜:数据树的未更改部分不需要复制,并且在内存中与相同状态的旧版本共享
- 利用 produce 函数,它将我们要更改的 state 作为第一个参数,对于第二个参数,我们传递一个名为 recipe 的函数,该函数传递一个 draft 参数,我们可以对其应用直接的 mutations。一旦 recipe 执行完成,这些 mutations 被记录并用于产生下一个状态。 produce 将负责所有必要的复制,并通过冻结数据来防止未来的意外修改
Immer是如何工作的?
- 使用 Immer,您会将所有更改应用到临时 draft,它是 currentState 的代理。一旦你完成了所有的 mutations,Immer 将根据对 draft state 的 mutations 生成 nextState。这意味着您可以通过简单地修改数据来与数据交互,同时保留不可变数据的所有好处
- 使用 Immer 就像拥有一个私人助理。助手拿一封信(当前状态)并给您一份副本(草稿)以记录更改。完成后,助手将接受您的草稿并为您生成真正不变的最终字母(下一个状态)
Immer优点
- 深度更新轻而易举
- 开箱即用的结构共享
- 开箱即用的对象冻结
- 代码简洁
- 小巧:3KB gzip
使用
state + Immer
useImmer
- 替代 state
- 本文开始的例子使用 useImmer
import { FC, useEffect } from 'react';
import { useImmer } from '@hooks/useImmer';
const App: FC = () => {
const [list, setList] = useImmer({ a: 1 });
useEffect(() => {
// setList({ a: 1 });
setList(draft => {
draft.a = 1;
});
}, []);
console.log('测试');
return (
<>
<h1>Hello Web3 React {list.a}</h1>
</>
);
};
export default App;
观察控制台,发现已经没有无意义渲染的提示了,It's wonderful!
- 注意:Immer 并没有 Immutable 解决的那么彻底,我们不能直接给draft赋值,而在draft上操作是极好的
useEffect(() => {
// setList({ a: 1 });
setList(draft => {
draft.a = 1;
});
},[]);
自己动手实现hooks-useImmer
import { useCallback, useState } from 'react';
import { produce, Draft, freeze } from 'immer';
export type DraftFunction<S> = (draft: Draft<S>) => void;
export type Updater<S> = (arg: S | DraftFunction<S>) => void;
export type ImmerHook<S> = [S, Updater<S>];
export function useImmer<S = any>(initialState: S | (() => S)): ImmerHook<S>;
export function useImmer<T>(initialState: T) {
// const [state, setState] = useState(initialState);
// 冻结 state,第二参数 true,表示深度冻结,对象不能修改了
const [value, updateValue] = useState(() =>
freeze(typeof initialState === 'function' ? initialState() : initialState, true)
);
// 使用 useCallback,放置组件间传递,产生句柄
return [
value,
useCallback((updater: Updater<T>) => {
if (typeof updater === 'function') {
updateValue(produce(updater));
} else {
updateValue(freeze(updater));
}
}, []),
];
}
以上就是一文详解React渲染优化之useImmer的详细内容,更多关于React渲染优化useImmer的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341