一、 useContext 和 useReducer
如果使用回调函数作为参数传递的话,因为每次 render 函数都会变化,也会导致子组件 render。当然我们可以使用useCallback
解决这个问题,但相比useCallbackReact
官方更推荐使用useReducer
,因为React会保证dispatch始终是不变的,不会引起组件的 render。
1.1 介绍和基本使用
参考:React官网文档
或者我之前博客:
useContext 介绍和基本使用
useReducer 介绍和基本使用
二、使用
Context.tsx
创建一个 Context 对象 (React.createContext
),当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
import { createContext } from "react";
export const ReducerContext = createContext(null);
index.tsx
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
Provider 接收一个 value 属性,传递给消费组件。
// index.tsx
import { useReducer } from 'react';
import ChildComponent from './ChildComponent';
import { ReducerContext } from './Context';
const initialData = {
isLogin: false,
error: '',
}
const loginReducer = (state, action) => {
switch (action.type) {
case 'success':
return {
...state,
isLogin: true,
message: '登陆成功!'
}
case 'fail':
return {
...state,
isLogin: false,
message: '登陆失败!'
}
default:
return state;
}
}
const index = () => {
const [state, dispatch] = useReducer(loginReducer, initialData);
return (
<div>
Reducer2:
<p>登陆状态:{isLogin ? '成功' : '失败'}</p>
<ReducerContext.Provider value={dispatch}>
<ChildComponent />
</ReducerContext.Provider>
</div>
)
}
export default index;
ChildComponent.tsx
嵌套消费组件层级 (未订阅 useContext
变化)
此层组件并未订阅 ReducerContext,但 当组件上层最近的
<ReducerContext.Provider>
更新时,会触发页面 render,从而导致此页面 render,所以使用memo
对组件进行包裹。
参考:React 中 memo()、useCallback()、useMemo()
// ChildComponent.tsx
import { memo } from "react";
import Child1 from './Child1';
import Child2 from './Child1';
const ChildComponent = () => {
console.log('ChildComponent');
return (
<>
<Child1 />
<Child1 />
</>
)
}
// 使用 `memo`
export default memo(ChildComponent);
Child1.tsx
组件中更新 ReducerContext
// Child1.tsx
import { useContext } from "react";
import { ReducerContext } from "./Context";
const Child1 = () => {
console.log('Child1');
const dispatch = useContext(ReducerContext)
return (
<>
<button onClick={()=>dispatch({type:'success'})}>成功</button>
<button onClick={()=>dispatch({type:'fail'})}>失败</button>
</>
)
}
export default Child1;
总结
- 页面state很简单,可以直接使用
useState
- 页面state比较复杂(state是一个对象或者state非常多散落在各处)请使用
userReducer
- 页面组件层级比较深,并且需要子组件触发state的变化,可以考虑
useReducer
+useContext