一文詳解React渲染優(yōu)化之useImmer
從一個(gè)例子開(kāi)始
import { FC, useState, useEffect } from 'react'; const App: FC = () => { const [list, setList] = useState({ a: 1 }); useEffect(() => { setList({ a: 1 }); }, []); console.log('測(cè)試'); return ( <> <h1>Hello Web3 React {list.a}</h1> </> ); }; export default App;
- 看控制臺(tái)輸出結(jié)果
- 因?yàn)閘ist是引用對(duì)象,雖然內(nèi)部的值相同,但引用地址變化了,因此react認(rèn)為state發(fā)生了變化,因此觸發(fā)了渲染,這也聯(lián)想到如果我們傳遞props時(shí)為對(duì)象或數(shù)組時(shí),會(huì)造成我們非期望渲染的緣由,那我們?cè)撊绾谓鉀Q這個(gè)問(wèn)題呢?
- 在此之前,先介紹一個(gè)react渲染檢查工具 why-did-you-render
渲染檢查工具
why-did-you-render, 可以在開(kāi)發(fā)時(shí),幫你檢測(cè)無(wú)意義的渲染
- 安裝
yarn add @welldone-software/why-did-you-render -D
- src下創(chuàng)建 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.上面的例子,輸出結(jié)果,顯示無(wú)意義渲染
要解決這個(gè)問(wèn)題,需要我們使用到Immer
Immer 與 UseImmer
- 不可變數(shù)據(jù):不可變數(shù)據(jù)的特點(diǎn)就是產(chǎn)出地址不同的對(duì)象引用,同時(shí)盡可能保留沒(méi)必要更新的屬性或者子屬性,這樣在你的組件樹(shù)中如果有數(shù)據(jù)的其他屬性或者子熟悉的依賴(lài)存在的話,可以避免一些rerender
- immutable:不改變?cè)瓟?shù)據(jù),將變化的部分與不變的部分組成一個(gè)新的數(shù)據(jù)對(duì)象**
- Immer:簡(jiǎn)化了不可變數(shù)據(jù)結(jié)構(gòu)的處理,與immutable代碼簡(jiǎn)單,去掉樹(shù)的壓縮,使用proxy代理,組合新的樹(shù)
- immer 可以在需要使用不可變數(shù)據(jù)結(jié)構(gòu)的任何上下文中使用,例如:React state、Redux等
- 不可變的數(shù)據(jù)結(jié)構(gòu)允許(高效)的變化檢測(cè):如果對(duì)象的引用沒(méi)有改變,那么對(duì)象本身也沒(méi)有改變。此外,它使克隆對(duì)象相對(duì)便宜:數(shù)據(jù)樹(shù)的未更改部分不需要復(fù)制,并且在內(nèi)存中與相同狀態(tài)的舊版本共享
- 利用 produce 函數(shù),它將我們要更改的 state 作為第一個(gè)參數(shù),對(duì)于第二個(gè)參數(shù),我們傳遞一個(gè)名為 recipe 的函數(shù),該函數(shù)傳遞一個(gè) draft 參數(shù),我們可以對(duì)其應(yīng)用直接的 mutations。一旦 recipe 執(zhí)行完成,這些 mutations 被記錄并用于產(chǎn)生下一個(gè)狀態(tài)。 produce 將負(fù)責(zé)所有必要的復(fù)制,并通過(guò)凍結(jié)數(shù)據(jù)來(lái)防止未來(lái)的意外修改
Immer是如何工作的?
- 使用 Immer,您會(huì)將所有更改應(yīng)用到臨時(shí) draft,它是 currentState 的代理。一旦你完成了所有的 mutations,Immer 將根據(jù)對(duì) draft state 的 mutations 生成 nextState。這意味著您可以通過(guò)簡(jiǎn)單地修改數(shù)據(jù)來(lái)與數(shù)據(jù)交互,同時(shí)保留不可變數(shù)據(jù)的所有好處
- 使用 Immer 就像擁有一個(gè)私人助理。助手拿一封信(當(dāng)前狀態(tài))并給您一份副本(草稿)以記錄更改。完成后,助手將接受您的草稿并為您生成真正不變的最終字母(下一個(gè)狀態(tài))
Immer優(yōu)點(diǎn)
- 深度更新輕而易舉
- 開(kāi)箱即用的結(jié)構(gòu)共享
- 開(kāi)箱即用的對(duì)象凍結(jié)
- 代碼簡(jiǎn)潔
- 小巧:3KB gzip
使用
state + Immer
useImmer
- 替代 state
- 本文開(kāi)始的例子使用 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('測(cè)試'); return ( <> <h1>Hello Web3 React {list.a}</h1> </> ); }; export default App;
觀察控制臺(tái),發(fā)現(xiàn)已經(jīng)沒(méi)有無(wú)意義渲染的提示了,It's wonderful!
- 注意:Immer 并沒(méi)有 Immutable 解決的那么徹底,我們不能直接給draft賦值,而在draft上操作是極好的
useEffect(() => { // setList({ a: 1 }); setList(draft => { draft.a = 1; }); },[]);
自己動(dòng)手實(shí)現(xiàn)hooks-useImmer
import { useCallback, useState } from 'react'; import { produce, Draft, freeze } from 'immer'; /** * 定義函數(shù)的簽名 * initialState 可以是一個(gè)數(shù)據(jù),也可以是一個(gè)函數(shù) * useImmer 返回元組,類(lèi)似 [state, setState] = useImmer(data); * Draft 中間狀態(tài) * (() => S) 自執(zhí)行函數(shù) */ 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>; /** * 實(shí)現(xiàn) * @param initialState */ export function useImmer<T>(initialState: T) { // const [state, setState] = useState(initialState); // 凍結(jié) state,第二參數(shù) true,表示深度凍結(jié),對(duì)象不能修改了 const [value, updateValue] = useState(() => freeze(typeof initialState === 'function' ? initialState() : initialState, true) ); // 使用 useCallback,放置組件間傳遞,產(chǎn)生句柄 return [ value, useCallback((updater: Updater<T>) => { if (typeof updater === 'function') { updateValue(produce(updater)); } else { updateValue(freeze(updater)); } }, []), ]; }
以上就是一文詳解React渲染優(yōu)化之useImmer的詳細(xì)內(nèi)容,更多關(guān)于React渲染優(yōu)化useImmer的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
每天一個(gè)hooks學(xué)習(xí)之useUpdateEffect
這篇文章主要為大家介紹了每天一個(gè)hooks學(xué)習(xí)之useUpdateEffect使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05create-react-app項(xiàng)目配置全解析
這篇文章主要為大家介紹了create-react-app項(xiàng)目配置全解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06React如何利用相對(duì)于根目錄進(jìn)行引用組件詳解
這篇文章主要給大家介紹了關(guān)于React如何使用相對(duì)于根目錄進(jìn)行引用組件的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10antd?table動(dòng)態(tài)修改表格高度的實(shí)現(xiàn)
本文主要介紹了antd?table動(dòng)態(tài)修改表格高度的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07