useReducer(reducer, initialArg, init?)
useReducer在组件的顶层调用以使用reducer管理state。
import { useReducer } from 'react';
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...
参数
reducer: 指定状态如何更新的 reducer 函数。纯函数,应该将状态和动作作为参数,并且应该返回下一个状态。state和action可以是任何类型。initialArg: 计算初始状态的值。它可以是任何类型的值。如何从中计算初始状态取决于下一个init参数。- optional
init: 指定如何计算初始状态的初始化函数。如果未指定,则初始状态设置为initialArg. 否则,初始状态设置为调用的结果init(initialArg)。
返回值
useReducer返回一个两个值的数组:
- 当前state。在第一次渲染期间,它被设置为
init(initialArg)orinitialArg(如果没有init)。 - dispatch函数,将状态更新为不同的值并触发重新渲染。
注意事项
useReducer是一个 Hook,所以你只能在组件的顶层或自己的 Hooks 中调用。不能在循环或条件中调用它。如果需要,请提取一个新组件并将状态移动到其中。- 在严格模式下,React 会调用你的 reducer 和 initializer 两次,以保证结果正确。这是仅限开发的行为,不会影响生产。所以这对我们的 reducer 和 initializer 提出了要求,即必须是纯函数,这样的话就不影响你的组件的逻辑。如果函数不纯,即两次获得的返回值不同的话,就会在两个值中二选一,忽略其中一个调用的结果,这往往是我们不想看到的结果。
规范
switch (action.type) {
case 'incremented_age': {
return {
name: state.name,
age: state.age + 1
//不建议直接更改状态
};
}
switch (action.type) {
case 'incremented_age': {
// ✅ Instead, return a new object
return {
...state,
age: state.age + 1
//正确姿势
};
避免对初始状态进行直接计算
初始状态保存一次,下一次渲染时忽略。
function createInitialState(username) {
// ...
}
function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, createInitialState(username));
// ...
如果按上述代码运行,在每次渲染时调用 createInitialState(username)函数。在创建大型数组或执行大量计算的情况下,效率就会变的很低
为了解决这个问题,将计算过程作为初始化函数传递给useReducer第三个参数:
function createInitialState(username) {
// ...
}
function TodoList({ username }) {
const [state, dispatch] = useReducer(reducer, null, createInitialState);
// ...
}
传递的是函数本身,而不是createInitialState()。初始化后不会重新创建初始状态。
在上面的例子中,createInitialState接受一个username参数。如果不需要任何信息,只需要第三函数的返回值来作为state,可以将null其作为第二个参数传递给useReducer.
useContext()和useReducer()组合代替Redux来管理状态
// reducer
const reducer = (state, action) => {
switch(action.type) {
case 'change':
return action.color
default:
return state
}
}
/**
* 创建一个 Color 组件
* Color 组件包裹的所有组件都可以访问到 value
*/
export const Color = props => {
const [color, dispatch] = useReducer(reducer, 'blue')
return (
<ColorContext.Provider value={{color, dispatch}}>
{props.children}
</ColorContext.Provider>
);
};
意思就是,将useContext()作为穿透作用域的工具,里面传入useReducer()
使用useReducer()来对状态进行初始化和操作管理