網站首頁 編程語言 正文
一、useCallback的作用
usecallback
不是用來解決組件中有過多內部函數導致的性能問題:
1.我們要知道,js創建一個函數的成本是非常小的,這點計算對于計算機來說是小case
2.其實使用useCallback會產成額外的性能:對deps的判斷
3.其實每次組件重新渲染時,都無所謂避免重新創建內部函數,因為即使useCallback的deps沒有變,它也會重新創建內部函數作為useCallback
的實參
那么,它的作用到底是什么?useCallback
的作用其實是用來避免子組件不必要的reRender:
首先,假如我們不使用useCallback,在父組件中創建了一個名為handleClick的事件處理函數,根據需求我們需要把這個handleClick傳給子組件,當父組件中的一些state變化后(這些state跟子組件沒有關系),父組件會reRender,然后會重新創建名為handleClick函數實例,并傳給子組件,這時即使用React.memo把子組件包裹起來,子組件也會重新渲染,因為props已經變化了,但這個渲染是無意義的
如何優化呢?這時候就可以用useCallback了,我們用useCallback把函數包起來之后,在父組件中只有當deps變化的時候,才會創建新的handleClick實例,子組件才會跟著reRender(注意,必須要用React.memo把子組件包起來才有用,否則子組件還是會reRender。React.memo是類似于class組件中的Pure.Component的作用)
對于這種deps不是經常變化的情況,我們用useCallback和React.memo的方式可以很好地避免子組件無效的reRender。但其實社區中對這個useCallback的使用也有爭議,比如子組件中只是渲染了幾個div,沒有其他的大量計算,而瀏覽器去重新渲染幾個dom的性能損耗其實也是非常小的,我們花了這么大的勁,使用了useCallback和React.memo,換來的收益很小,所以一些人認為就不用useCallback,就讓瀏覽器去重新渲染好了。至于到底用不用,此處不深入討論,我的建議是當子組件中的dom數量很多,或者有一些大量的計算操作,是可以進行這樣的優化的。
以上都是討論的deps不會經常改變的情況的優化,而很多時候useCallback中的deps數組中的變量是會經常改變的,這個時候我們用useCallback已經沒啥意義了,反而會造成性能損耗(deps判斷)。有沒有什么辦法可以讓子組件不重新渲染,也能拿到父組件中handleClick函數中的最新state值呢?下面我們討論useRef,useReducer,usePersistFn這三種解決方法
二、useRef解決方案
為了讓子組件不進行reRender,我們必須保證
1.父組件不會重新創建handleClick函數實例
2.handleClick函數能拿到最新的state
使用useRef
const [text, setText] = useState('Initial value'); const textRef = useRef(text); const handleClick= useCallback(() => { console.log(textRef.current); }, []); useEffect(() => { console.log('update text') textRef.current = text; }, [text])
textRef在每次reRender時不會改變,這樣我們把handleClick傳給子組件,handleClick函數中每次都能拿到父組件中最新的state
三、useReducer解決方案
使用useReducer
function reducer(state, action) { switch(action.type) { case 'update': return action.preload; case 'childComponent': // 要執行的函數 return state; } } export default function Index() { // 父組件 const [state, dispatch] = useReducer(reducer, 'Initial value'); return ( <> <input value={state} onChange={(e) => dispatch({ type: 'update', preload: e.target.value })} /> <ChildComponent dispatch={dispatch} /> </> ) } //在 ChildComponent中,拿到dispatch,通過dispatch({type: 'childComponent' })的方式調用
dispatch自帶memoize,所以子組件不會進行 re-render
四、usePersistFn解決方案
usePersistFn是aooks庫中的一個鉤子函數,它接收一個函數,返回一個永遠不變的函數引用,在這個函數中每次都能拿到最新的state值,看看usePersistFn的源碼:
function usePersistFn(fn) { const fnRef = useRef(fn); fnRef.current = fn; const persistFn = useRef(); if (!persistFn.current) { persistFn.current = function (...args) { return fnRef.current.apply(this, args); }; } return persistFn.crrent; }
這里它用了兩個useRef,保證返回的函數引用不變,并且每次函數內部能拿到最新的state。
這里可以用useRef和useCallback到達同樣的效果:
function usePersistFn(fn) { const fnRef = useRef(); fnRef.current = fn; const persist= useCallback((...rest) => { return fnRef.current(...rest); }, []); return persist }
這種方法和上面的useRef解決方案差不多,只是封裝了起來而已
另外,本文介紹一下useContext使用的問題和優化:
問題:當context中的值改變時,只要使用useContext訂閱了context的組件,不管該組件用到的state改不改變,該組件都會reRender,此時用React.memo是沒有辦法優化的。
優化:
1.拆分context,把經常改變的數據和不經常改變的數據拆分開,在只使用穩定數據的組件中,我們只使用stableContext
2.使用useMemo
const {state}= useContext(AppContext); return useMemo(() => <span>data:{state.depData}</span>, [state.depData]);
3.如何有效減少使用useContext導致的不必要渲染
總結
原文鏈接:https://blog.csdn.net/Kobe_G/article/details/121752526
相關推薦
- 2022-10-01 Golang?中的?unsafe.Pointer?和?uintptr詳解_Golang
- 2022-04-01 Android實現字母導航控件的示例代碼_Android
- 2022-08-17 Android?Flutter表格組件Table的使用詳解_Android
- 2023-02-25 React18之update流程從零實現詳解_React
- 2024-03-10 詳解Spring Bean的生命周期
- 2022-12-03 Golang檢查變量類型的四種方式_Golang
- 2023-01-10 Flutter?CustomPaint繪制widget使用示例_IOS
- 2022-07-21 SystemVerilog中使用string所遇到的問題
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支