React?Hook?四種組件優(yōu)化總結
前言
React Hook 已成為當前最流行的開發(fā)范式,React 16.8 以后基于 Hook 開發(fā)極大簡化開發(fā)者效率,同時不正確的使用 React Hook也帶來了很多的性能問題,本文梳理基于 React Hook 開發(fā)組件的過程中如何提高性能。
組件抽取
優(yōu)化前
每次點擊 Increase 都會引起子組件 Child 的渲染,哪怕子組件并沒有狀態(tài)變化
function?Before(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計數器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<hr?/>
????????????<Child?name={name}/>
????????</div>
????)
}
//?子組件
function?Child(props){
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????</div>
????)
}
優(yōu)化后
只需要把 Increase 抽取成獨立的組件即可。此時點擊按鈕,子組件并不會渲染。
/**
?*?優(yōu)化后,Increase提取以后,上下文發(fā)生變化,組件內
?*?@returns?
?*/
function?Increase(){
????console.log('Child?Increase')
????let?[count,setCount]?=?useState(0)
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計數器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????</div>
????)
}
function?After(){
????console.log('Demo1?Parent')
????let?[name,setName]?=?useState('-')
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????return?(
????????<div>
????????????<Increase/>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}/>
????????</div>
????)
}
//?子組件
function?Child(props){
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????</div>
????)
}memo 優(yōu)化組件
同樣基于上述優(yōu)化前代碼,如果不抽取組件,使用 memo 優(yōu)化后,當點擊按鈕后,也不會觸發(fā)二次渲染。
//?優(yōu)化前
function?AfterMemo(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計數器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}/>
????????</div>
????)
}
//?子組件
const?Child?=?memo((props)=>{
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????</div>
????)
})React.memo 語法
React.memo 為高階組件,與 React.PureComponent相似。
function?TestComponent(props){
??//?使用?props?渲染
}
function?areEqual(prevProps,nextProps){
??/*
??如果把?nextProps?傳入?render?方法的返回結果與
??將?prevProps?傳入?render?方法的返回結果一致則返回?true,
??否則返回?false
??*/
}
export?default?React.memo(TestComponent,areEqual)與 class 組件中 shouldComponentUpdate() 方法不同的是,如果 props 相等,areEqual 會返回 true;如果 props 不相等,則返回 false。這與 shouldComponentUpdate 方法的返回值相反。
useCallback 優(yōu)化組件
如果已經用了 memo ,當遇到下面這種場景時,同樣會觸發(fā)子組件渲染。比如,給 Child 綁定一個 handleClick ,子組件內部增加一個按鈕,當點擊子組件的按鈕時,更改 count 值,即使沒有發(fā)生 name 變化,也同樣會觸發(fā)子組件渲染,為什么?memo 不是會判斷 name 變化了,才會更新嗎?
function?Before(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????const?handleChange?=?()=>{
????????setCount(count+1)
????}
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計數器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}?handleClick={handleChange}/>
????????</div>
????)
}
//?子組件
const?Child?=?memo((props)=>{
????console.log('Demo1?Child')
????return?(
????????<div?className='l50'>
????????????子組件渲染:{props.name}
????????????<button?onClick={props.handleClick}>更改count</button>
????????</div>
????)
})
并不是 memo 沒有生效,是因為當狀態(tài)發(fā)生變化時,父組件會從新執(zhí)行,導致從新創(chuàng)建了新的handleChange 函數,而 handleChange 的變化導致了子組件的再次渲染。
優(yōu)化后
點擊父組件的Increase按鈕,更改了 count 值,經過 useCallback 包裹 handleChange 函數以后,我們會發(fā)現(xiàn)子組件不再渲染,說明每當父組件執(zhí)行的時候,并沒有創(chuàng)建新的 handleChange 函數,這就是通過 useCallback 優(yōu)化后的效果。 即使我們點擊子組件的按鈕,也同樣不會觸發(fā)子組件的渲染,同樣 count 會進行累加。
function?After(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????let?text?=?useRef();
????let?[name,setName]?=?useState('-')
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?handleInput?=?(e)=>{
????????setName(e.target.value)
????}
????const?handleChange?=?useCallback(()=>{
????????//?為了讓?count?能夠累加,我們使用ref?獲取值
????????let?val?=?parseInt(text.current.textContent);
????????setCount(val+1)
????},[])
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計數器:</label>
????????????????<span?className='mr10'?ref={text}>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div?className='l50'>
????????????????<label?htmlFor="">改變子組件:</label>
????????????????<input?type="text"?value={name}?onChange={handleInput}/>
????????????</div>
????????????<Child?name={name}?handleClick={handleChange}/>
????????</div>
????)
}
useCallback 作用
//?用法
useCallback(()=>{
??//?to-do
},[])
//?示例
function?App(){
??//?點擊按鈕調用此函數,但返回被緩存
??const?onClick?=?useCallback(()?=>?{
????console.log('我被緩存了,怎么點擊都返回一樣');
??},?[]);
??return?(?
????<button?onClick={onClick}>點擊</button>
??);
}useCallback接收 2 個參數,第一個為緩存的函數,第二個為依賴值- 主要用于緩存函數,第二次會返回同樣的結果。
useMemo 優(yōu)化
我們定義了一個total函數,內部使用 1 填充了100次,通過 reduce 計算總和,經過測試發(fā)現(xiàn)點擊 Increase按鈕后,只會執(zhí)行 total1 ,不會執(zhí)行 total2,假設total計算量巨大,就會造成內存的浪費,通過 useMemo 可以幫我們緩存計算值。
function?Before(){
????console.log('Demo1?Parent')
????let?[count,setCount]?=?useState(0)
????const?handleClick?=?()=>{
????????setCount(count+1)
????}
????const?total1?=?()=>{
????????console.log('計算求和1')
????????let?arr?=?Array.from({?length:100?}).fill(1)
????????return?arr.reduce((prev,next)=>prev+next,0)
????}
????//?緩存對象值
????const?total2?=?useMemo(()=>{
????????console.log('計算求和2')
????????let?arr?=?Array.from({?length:100?}).fill(1)
????????return?arr.reduce((prev,next)=>prev+next,0)
????},[count])
????return?(
????????<div>
????????????<div?className='l50'>
????????????????<label>計數器:</label>
????????????????<span?className='mr10'>{count}</span>
????????????????<button?className='ml10'?onClick={handleClick}>Increase</button>
????????????</div>
????????????<div>
????????????????<label>總和:</label>
????????????????<span>{total1()}</span>
????????????????<span>{total2}</span>
????????????</div>
????????</div>
????)
}
useMemo 語法
const?memoizedValue?=?useMemo(()?=>?computeExpensiveValue(a,?b),?[a,?b]);
- 傳入一個函數進去,會返回一個
memoized值,需要注意的是,函數內必須有返回值 - 第二個參數會依賴值,當依賴值更新時,會從新計算。
useCallback 和 useMemo 區(qū)別
他們都用于緩存,useCallback 主要用于緩存函數,返回一個 緩存后 函數,而 useMemo 主要用于緩存值,返回一個緩存后的值。
到此這篇關于React Hook 四種組件優(yōu)化總結的文章就介紹到這了,更多相關React Hook 組件優(yōu)化內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解如何在項目中使用jest測試react native組件
本篇文章主要介紹了詳解如何在項目中使用jest測試react native組件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02
ReactHook使用useState更新變量后,如何拿到變量更新后的值
這篇文章主要介紹了ReactHook使用useState更新變量后,如何拿到變量更新后的值問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
react搭建環(huán)境時執(zhí)行npm start報錯start: 'react-scripts&
這篇文章主要介紹了react搭建環(huán)境時執(zhí)行npm start報錯start: 'react-scripts start'的解決方案,具有很好的參考價值,希望杜對大家有所幫助,2023-10-10

