【react】react 重复渲染及解决方案
react 重复渲染及解决方案
相关文章
- 浅谈 react fiber 架构
- 为什么说只能在最顶层使用 Hook
- useEffect 与 useLayoutEffect
- react 重复渲染及解决方案
- react hooks 是什么
- react 生命周期
- react 15-16 架构变化
- react 通信方式
- react api
- 为什么 React 渲染列表时需要加上 key
react 重复渲染及解决方案
一、哪些情况导致组件重复渲染
状态变化导致重复渲染
当组件的状态(state)发生变化时,React 会重新渲染组件,以便将新的状态反映在 UI 上。这是 React 中组件更新的内部机制,也是组件重复渲染的根本原因之一。
父组件导致重复渲染
当父组件重新渲染时,它的子组件也会跟着重新渲染。这是因为 React 默认情况下会比较新旧虚拟 DOM 树,以确定需要重新渲染的组件。
Context 变化导致重复渲染
当使用 Context API 时,如果 Context Provider 提供的 value 发生变化,使用该 Context 的所有组件都会重新渲染。即使组件只使用了 Context 中的部分数据,也会触发重新渲染。
二、如何避免不必要的重渲染
状态变化导致重复渲染
组件内部的状态改变会触发组件重新渲染,因此我们应该谨慎使用状态。只有在必要时才使用状态。如果某些值的变化不会影响页面渲染,我们可以将这些值保存在函数组件外部,或者使用 useRef。
1 |
|
父组件导致重复渲染
state影响最小化原则
尽量将状态的影响范围限制在最小的组件范围内。
优化前
1
2
3
4
5
6
7
8
9
10
11const Component = () => {
const [isOpen, setOpen] = useState(false)
return (
<div>
<button onClick={() => setOpen(!isOpen)}>open</button>
{ isOpen && <ModalDialog />}
{/* 状态的变化会引起 SlowComponent 重复渲染 */}
<SlowComponent />
</div>
)
}优化后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const Component = () => {
return (
<div>
<ButtonWithDialog />
<SlowComponent />
</div>
)
}
const ButtonWithDialog = () => {
const [isOpen, setOpen] = useState(false)
return (
<>
<button onClick={() => setOpen(!isOpen)}>open</button>
{ isOpen && <ModalDialog />}
</>
)
}使用React.memo缓存组件
优化前
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import React from 'react';
const ComponentWithoutMemo = ({ data }) => {
console.log('Rendering ComponentWithoutMemo');
return <div>{data}</div>;
};
function App() {
const data = 'Hello, World!';
return (
<div>
{/* 状态的变化会引起 ComponentWithoutMemo 重复渲染 */}
<ComponentWithoutMemo data={data} />
</div>
);
}
export default App;优化后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import React from 'react';
const ComponentWithoutMemo = React.memo(({ data }) => {
console.log('Rendering ComponentWithoutMemo');
return <div>{data}</div>;
});
function App() {
const data = 'Hello, World!';
return (
<div>
{/* 使用 React.memo 缓存组件,当 data 没有变化时,不会触发重复渲染 */}
<ComponentWithoutMemo data={data} />
</div>
);
}
export default App;使用useMemo缓存props
如果props是引用类型,则依然会发生重复渲染。因为这些引用类型的变量在每次父组件渲染时都会更新,此时则需要使用 useMemo 或 useCallback 缓存 props。
优化后
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import React from 'react';
const ComponentWithoutMemo = React.memo(({ data }) => {
console.log('Rendering ComponentWithoutMemo');
return <div>{data}</div>;
});
function App() {
const data = useMemo(() => ({ message : 'Hello, World!'}), []);
return (
<div>
{/* 使用 React.memo 缓存组件,当 data 没有变化时,不会触发重复渲染 */}
<ComponentWithoutMemo data={data} />
</div>
);
}
export default App;
Context 变化导致重复渲染
使用useMemo缓存content
优化前
1
2
3
4
5
6
7
8
9
10
11
12
13
14import React from 'react';
import Home from './home'
const MyContext = React.createContext({});
const App = () => {
const value = {
data: 'Hello, World!',
count: 0
};
return <MyContext.Provider value={value}>{Home}</MyContext.Provider>;
};
export default App优化后
1
2
3
4
5
6
7
8
9
10
11
12
13
14import React from 'react';
import Home from './home'
const MyContext = React.createContext({});
const App = () => {
const value = useMemo(() => ({
data: 'Hello, World!',
count: 0
}), []);
return <MyContext.Provider value={value}>{Home}</MyContext.Provider>;
};
export default Appcontext读写分离
和state类似,将context的影响范围限制在最小的组件范围内。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53import React, { createContext, useContext, useState } from 'react';
// 创建两个独立的 Context
const ReadDataContext = createContext();
const WriteDataContext = createContext();
// 读取数据的 Context Provider
const ReadDataProvider = ({ children }) => {
const [data, setData] = useState('');
const value = { data }
return <ReadDataContext.Provider value={value}>{children}</ReadDataContext.Provider>;
};
// 写入数据的 Context Provider
const WriteDataProvider = ({ children }) => {
const [data, setData] = useState('');
const value = { setData };
return <WriteDataContext.Provider value={value}>{children}</WriteDataContext.Provider>;
};
// 读取数据的子组件
const ReadDataComponent = () => {
const data = useContext(ReadDataContext);
return (
<div>
<p>{data}</p>
</div>
);
};
// 写入数据的子组件
const WriteDataComponent = () => {
const { setData } = useContext(WriteDataContext);
return (
<div>
<button onClick={setData}></button>
</div>
);
};
// 应用程序根组件
const App = () => (
<ReadDataProvider>
<WriteDataProvider>
<div>
<ReadDataComponent />
<WriteDataComponent />
</div>
</WriteDataProvider>
</ReadDataProvider>
);
export default App;多个数据场景
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43import { createContext, useContext, useReducer } from 'react'
const WriteContext = createContext(null)
const ReadContext = createContext(null)
const useWriteContext = () => useContext(WriteContext)
const useReadContext = () => useContext(ReadContext)
class initialState {
navHeight = 62
groupTabIndex = 0
errorData = {
show: false,
retcode: 9999,
}
}
/**
* 全局的 Reducer
*/
function pageReducer(state, action) {
switch (action.type) {
case '__setStore':
state = Object.assign(state, action.payload)
break
default:
console.error('reducer error', action)
return state
}
return { ...state }
}
export default function Context(props) {
const [state, dispatch] = useReducer(pageReducer, new initialState())
return (
<WriteContext.Provider value={dispatch}>
<ReadContext.Provider value={{ state }}>{props.children}</ReadContext.Provider>
</WriteContext.Provider>
)
}
export { useWriteContext, useReadContext }
【react】react 重复渲染及解决方案
https://www.cccccl.com/20231102/react/react 重复渲染及解决方案/