減少react組件不必要的重新渲染實(shí)現(xiàn)方法
什么是重新渲染
react重新渲染是當(dāng)你的組件已經(jīng)渲染在屏幕上,由于數(shù)據(jù)狀態(tài)變化,又重新觸發(fā)了組件的渲染。重新渲染是正常現(xiàn)象,但有時(shí)由于自身代碼問(wèn)題造成不必要的重新渲染。
不必要的重新渲染
由于錯(cuò)誤的代碼引發(fā)組件的重新渲染。例如:與react組件本身無(wú)關(guān)的狀態(tài)變化引起的組件的重新渲染。雖然react組件重新渲染了,但由于渲染很快,通常用戶(hù)并不會(huì)感知到。但如果重新渲染發(fā)生比較復(fù)雜的組件上,可能會(huì)導(dǎo)致界面卡頓,甚至造成長(zhǎng)時(shí)間的卡死現(xiàn)象。
react組件重新渲染情況
組件的重新渲染分為以下幾種情況:
state變化
count變化會(huì)引起組件的重新渲染。
const App = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount(count+1) } return <div onClick={handleClick}>{count}</div> }
父組件的重新渲染
當(dāng)組件的父組件重新渲染時(shí),該組件必定重新渲染。一般情況下子組件的渲染不會(huì)觸發(fā)父組件的渲染。
實(shí)際上props變化也是由于父組件狀態(tài)變化引起的,只要父組件重新渲染,子組件不管props有沒(méi)有變化都會(huì)重新渲染。(沒(méi)有使用優(yōu)化時(shí))
import { useState } from "react"; const Child = () => { console.log("子組件重新渲染"); const [childCount, setChildCount] = useState(0); const handleChildClick = () => { setChildCount(childCount + 1); }; return ( <> <div onClick={handleChildClick}>Child {childCount}</div> </> ); }; export default function StateChange() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; console.log("父組件重新渲染", count); return ( <div> <div onClick={handleClick}>parent {count}</div> <Child /> </div> ); }
context變化
當(dāng)context的Provider提供的值放生變化,所有使用該context的組件都將重新渲染。
import { createContext, useState, useContext, useMemo } from "react"; const Context = createContext({ val: 0 }); const Provider = ({ children }) => { const [val, setVal] = useState(0); const handleClick = () => { setVal(val + 1); }; const value = useMemo(() => { return { val: val }; }, [val]); return ( <Context.Provider value={value}> {children} <button onClick={handleClick}>context change</button> </Context.Provider> ); }; const useVal = () => useContext(Context); const Child1 = () => { const { val } = useVal(); console.log("Child1重新渲染", val); return <div>Child1</div>; }; const Child2 = () => { const { val } = useVal(); console.log("Child2重新渲染", val); return <div>Child2</div>; }; export default function ContextChange() { return ( <Provider> <Child1 /> <Child2 /> </Provider> ); }
在組件內(nèi)創(chuàng)建組件
?:這種做法非常消耗性能。當(dāng)組件重新渲染,內(nèi)部組件都會(huì)重新mount。這種做法容易導(dǎo)致很多bug出現(xiàn)。
import { useState, useEffect } from "react"; const Component = () => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; const InComponent = () => { console.log("內(nèi)部組件重新渲染"); useEffect(() => { console.log("內(nèi)部組件重新mount"); }, []); return <div>inComponent</div>; }; return ( <> <button onClick={onClick}>點(diǎn)我</button> <InComponent /> </> ); }; export default function ComInComponent() { return ( <> <Component /> </> ); }
一些減少重新渲染的方法
useState初始值使用函數(shù)形式
看一個(gè)例子:useState的初始值經(jīng)過(guò)a + 1計(jì)算得到: 此時(shí)的useState的參數(shù)是一個(gè)函數(shù)執(zhí)行,也就是一個(gè)值
import { useState } from "react"; function getInitState() { console.count("獲取初始化的值"); const a = 1; return a + 1; } const App = () => { const [value, setValue] = useState(getInitState()); const onChange = (event) => setValue(event.target.value); return <input type="text" value={value} onChange={onChange} />; }; export default App;
當(dāng)我們?cè)趇nput中繼續(xù)輸入,可以看到getInitState的console次數(shù):
當(dāng)我們把useState第一個(gè)參數(shù)改為函數(shù)時(shí):(該函數(shù)會(huì)自動(dòng)調(diào)用)
import { useState } from "react"; function getInitState() { console.count("獲取初始化的值"); const a = 1; return a + 1; } const App = () => { const [value, setValue] = useState(getInitState); const onChange = (event) => setValue(event.target.value); return <input type="text" value={value} onChange={onChange} />; }; export default App;
該函數(shù)只會(huì)在初始化的時(shí)候調(diào)用一次:
重新組織組件結(jié)構(gòu)
提取單獨(dú)的組件,獨(dú)自維護(hù)自己的狀態(tài),防止發(fā)生不必要的渲染。
import { useState } from "react"; const BigComponent = () => { console.log("一個(gè)非常復(fù)雜的大組件:渲染了"); return <div>BigComponent</div>; }; const AllComponent = () => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; return ( <> <p>重新渲染次數(shù): {state}</p> <button onClick={onClick}>點(diǎn)我</button> <BigComponent /> </> ); }; const ButtonComponent = () => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; return ( <> <p>重新渲染次數(shù): {state}</p> <button onClick={onClick}>點(diǎn)我</button> </> ); }; const SplitComponent = () => { return ( <> <ButtonComponent /> <BigComponent /> </> ); }; const App = () => { return ( <> <p>AllComponent是沒(méi)有重新組織的組件</p> <AllComponent /> <hr /> <p>SplitComponent是重新組織劃分的組件, 不會(huì)觸發(fā)大組件BigComponent的渲染</p> <SplitComponent /> </> ); }; export default App;
巧用props.children
利用props.children 來(lái)減少不必要的重復(fù)渲染。
import { useState } from "react"; const BigComponent = () => { console.log("一個(gè)非常復(fù)雜的大組件:渲染了"); return <div>BigComponent</div>; }; const AllComponent = () => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; return ( <> <p>重新渲染次數(shù): {state}</p> <button onClick={onClick}>點(diǎn)我</button> <BigComponent /> </> ); }; const ComponentWithChildren = ({ children }) => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; return ( <> <p>重新渲染次數(shù): {state}</p> <button onClick={onClick}>點(diǎn)我</button> {children} </> ); }; const SplitComponent = () => { return ( <ComponentWithChildren> <BigComponent /> </ComponentWithChildren> ); }; const App = () => { return ( <> <p>AllComponent是沒(méi)有重新組織的組件</p> <AllComponent /> <hr /> <p> SplitComponent是巧用了children的組件, 不會(huì)觸發(fā)大組件BigComponent的渲染 </p> <SplitComponent /> </> ); }; export default App;
把組件當(dāng)成props傳遞
把組件當(dāng)成props傳遞給其他組件,也可以減少渲染。(與children類(lèi)似)
import { useState } from "react"; const BigComponent = () => { console.log("一個(gè)非常復(fù)雜的大組件:渲染了"); return <div>BigComponent</div>; }; const AllComponent = () => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; return ( <> <p>重新渲染次數(shù): {state}</p> <button onClick={onClick}>點(diǎn)我</button> <BigComponent /> </> ); }; const ComponentWithProps = ({ comp }) => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; return ( <> <p>重新渲染次數(shù): {state}</p> <button onClick={onClick}>點(diǎn)我</button> {comp} </> ); }; const comp = <BigComponent />; const SplitComponent = () => { return ( <> <ComponentWithProps comp={comp} /> </> ); }; const App = () => { return ( <> <p>AllComponent是沒(méi)有重新組織的組件</p> <AllComponent /> <hr /> <p> SplitComponent是把組件當(dāng)成props傳遞, 不會(huì)觸發(fā)大組件BigComponent的渲染 </p> <SplitComponent /> </> ); }; export default App;
React.memo
memo 減少重復(fù)渲染,只要子組件的props沒(méi)有改變(淺比較)。
import { useState, memo } from "react"; const Child = () => { console.log("子組件重新渲染"); return ( <> <div>Child</div> </> ); }; const MemoChild = memo(Child); export default function ReactMemo() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); }; return ( <div> <div onClick={handleClick}>parent {count}</div> <MemoChild /> </div> ); }
如果props是一個(gè)object, array 或者 function,memo必須配合useMemo來(lái)緩存,單獨(dú)使用都不起作用。
當(dāng)然也可以單獨(dú)使用useMemo緩存整個(gè)組件。
import React, { useState, useMemo } from "react"; const Child = ({ value }) => { console.log("Child重新渲染", value.value); return <>{value.value}</>; }; const values = [1, 2, 3]; const UseMemp = () => { const [state, setState] = useState(1); const onClick = () => { setState(state + 1); }; // 使用useMemo緩存組件 const items = useMemo(() => { return values.map((val) => <Child key={val} value={{ value: val }} />); }, []); return ( <> <button onClick={onClick}>點(diǎn)我 {state}</button> <br /> {items} </> ); }; export default UseMemp;
useCallback
函數(shù)傳遞可以使用useCallback來(lái)緩存函數(shù)。
key值
在循環(huán)數(shù)組時(shí),有人使用index作為key,可以這樣做,但是你要保證你的數(shù)組是靜態(tài)的,沒(méi)有新增,刪除,插入,排序等情況,否則不能使用index作為key。
完整的代碼:
參考
以上就是減少react組件不必要的重新渲染實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于減少react組件重新渲染的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React中獲取數(shù)據(jù)的3種方法及優(yōu)缺點(diǎn)
這篇文章主要介紹了React中獲取數(shù)據(jù)的3種方法及優(yōu)缺點(diǎn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02react中代碼塊輸出,代碼高亮顯示,帶行號(hào),能復(fù)制的問(wèn)題
這篇文章主要介紹了react中代碼塊輸出,代碼高亮顯示,帶行號(hào),能復(fù)制的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09Reactjs實(shí)現(xiàn)通用分頁(yè)組件的實(shí)例代碼
這篇文章主要介紹了Reactjs實(shí)現(xiàn)通用分頁(yè)組件的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下吧2017-01-01react實(shí)現(xiàn)antd線(xiàn)上主題動(dòng)態(tài)切換功能
這篇文章主要介紹了react實(shí)現(xiàn)antd線(xiàn)上主題動(dòng)態(tài)切換功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08React項(xiàng)目build打包頁(yè)面空白的解決方案
React項(xiàng)目執(zhí)行build命令后,在本地服務(wù)器打開(kāi)頁(yè)面是空白的,本文主要介紹了React項(xiàng)目build打包頁(yè)面空白的解決方案,感興趣的可以了解一下2023-08-08