網站首頁 編程語言 正文
什么是重新渲染
react重新渲染是當你的組件已經渲染在屏幕上,由于數據狀態變化,又重新觸發了組件的渲染。重新渲染是正常現象,但有時由于自身代碼問題造成不必要的重新渲染。
不必要的重新渲染
由于錯誤的代碼引發組件的重新渲染。例如:與react組件本身無關的狀態變化引起的組件的重新渲染。雖然react組件重新渲染了,但由于渲染很快,通常用戶并不會感知到。但如果重新渲染發生比較復雜的組件上,可能會導致界面卡頓,甚至造成長時間的卡死現象。
react組件重新渲染情況
組件的重新渲染分為以下幾種情況:
state變化
count變化會引起組件的重新渲染。
const App = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count+1)
}
return <div onClick={handleClick}>{count}</div>
}
父組件的重新渲染
當組件的父組件重新渲染時,該組件必定重新渲染。一般情況下子組件的渲染不會觸發父組件的渲染。
實際上props變化也是由于父組件狀態變化引起的,只要父組件重新渲染,子組件不管props有沒有變化都會重新渲染。(沒有使用優化時)
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變化
當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>
);
}
在組件內創建組件
?:這種做法非常消耗性能。當組件重新渲染,內部組件都會重新mount。這種做法容易導致很多bug出現。
import { useState, useEffect } from "react";
const Component = () => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
const InComponent = () => {
console.log("內部組件重新渲染");
useEffect(() => {
console.log("內部組件重新mount");
}, []);
return <div>inComponent</div>;
};
return (
<>
<button onClick={onClick}>點我</button>
<InComponent />
</>
);
};
export default function ComInComponent() {
return (
<>
<Component />
</>
);
}
一些減少重新渲染的方法
useState初始值使用函數形式
看一個例子:useState的初始值經過a + 1計算得到: 此時的useState的參數是一個函數執行,也就是一個值
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;
當我們在input中繼續輸入,可以看到getInitState的console次數:
當我們把useState第一個參數改為函數時:(該函數會自動調用)
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;
該函數只會在初始化的時候調用一次:
重新組織組件結構
提取單獨的組件,獨自維護自己的狀態,防止發生不必要的渲染。
import { useState } from "react";
const BigComponent = () => {
console.log("一個非常復雜的大組件:渲染了");
return <div>BigComponent</div>;
};
const AllComponent = () => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<>
<p>重新渲染次數: {state}</p>
<button onClick={onClick}>點我</button>
<BigComponent />
</>
);
};
const ButtonComponent = () => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<>
<p>重新渲染次數: {state}</p>
<button onClick={onClick}>點我</button>
</>
);
};
const SplitComponent = () => {
return (
<>
<ButtonComponent />
<BigComponent />
</>
);
};
const App = () => {
return (
<>
<p>AllComponent是沒有重新組織的組件</p>
<AllComponent />
<hr />
<p>SplitComponent是重新組織劃分的組件, 不會觸發大組件BigComponent的渲染</p>
<SplitComponent />
</>
);
};
export default App;
巧用props.children
利用props.children 來減少不必要的重復渲染。
import { useState } from "react";
const BigComponent = () => {
console.log("一個非常復雜的大組件:渲染了");
return <div>BigComponent</div>;
};
const AllComponent = () => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<>
<p>重新渲染次數: {state}</p>
<button onClick={onClick}>點我</button>
<BigComponent />
</>
);
};
const ComponentWithChildren = ({ children }) => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<>
<p>重新渲染次數: {state}</p>
<button onClick={onClick}>點我</button>
{children}
</>
);
};
const SplitComponent = () => {
return (
<ComponentWithChildren>
<BigComponent />
</ComponentWithChildren>
);
};
const App = () => {
return (
<>
<p>AllComponent是沒有重新組織的組件</p>
<AllComponent />
<hr />
<p>
SplitComponent是巧用了children的組件, 不會觸發大組件BigComponent的渲染
</p>
<SplitComponent />
</>
);
};
export default App;
把組件當成props傳遞
把組件當成props傳遞給其他組件,也可以減少渲染。(與children類似)
import { useState } from "react";
const BigComponent = () => {
console.log("一個非常復雜的大組件:渲染了");
return <div>BigComponent</div>;
};
const AllComponent = () => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<>
<p>重新渲染次數: {state}</p>
<button onClick={onClick}>點我</button>
<BigComponent />
</>
);
};
const ComponentWithProps = ({ comp }) => {
const [state, setState] = useState(1);
const onClick = () => {
setState(state + 1);
};
return (
<>
<p>重新渲染次數: {state}</p>
<button onClick={onClick}>點我</button>
{comp}
</>
);
};
const comp = <BigComponent />;
const SplitComponent = () => {
return (
<>
<ComponentWithProps comp={comp} />
</>
);
};
const App = () => {
return (
<>
<p>AllComponent是沒有重新組織的組件</p>
<AllComponent />
<hr />
<p>
SplitComponent是把組件當成props傳遞, 不會觸發大組件BigComponent的渲染
</p>
<SplitComponent />
</>
);
};
export default App;
React.memo
memo 減少重復渲染,只要子組件的props沒有改變(淺比較)。
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是一個object, array 或者 function,memo必須配合useMemo來緩存,單獨使用都不起作用。
當然也可以單獨使用useMemo緩存整個組件。
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}>點我 {state}</button>
<br />
{items}
</>
);
};
export default UseMemp;
useCallback
函數傳遞可以使用useCallback來緩存函數。
key值
在循環數組時,有人使用index作為key,可以這樣做,但是你要保證你的數組是靜態的,沒有新增,刪除,插入,排序等情況,否則不能使用index作為key。
完整的代碼:
codesandbox.io/s/hidden-me…
參考
reactjs.org/docs/hooks-…
medium.com/@guptagarud…
原文鏈接:https://juejin.cn/post/7183490342144966712
相關推薦
- 2022-05-01 Windows系統安裝redis數據庫_Redis
- 2022-08-30 Spark中緩存和檢查點的區別
- 2022-04-25 搭建RocketMQ在本地IDEA開發調試環境教程_服務器其它
- 2023-06-04 C#中+=是什么意思及+=的用法_C#教程
- 2023-02-17 Python排序算法之堆排序算法_python
- 2022-11-18 C++11正則表達式詳解(regex_match、regex_search和regex_replac
- 2022-08-05 springBoot集成swagger啟動報錯:Failed to start bean ‘docu
- 2022-01-15 跨域系列之proxy代理,解決跨域的方法之一
- 最近更新
-
- 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同步修改后的遠程分支