useReducer 与 useState 对比选择 useReducer() 而非 useState() 的 3 个理由
它是什么
useReducer()
是 React Hooks API 中的一个方法,与 类似,useState
但能让你更好地控制状态。它接受一个 reducer 函数和初始状态作为参数,并返回状态和 dispatch 方法:
const [state, dispatch] = React.useReducer(reducerFn, initialState, initFn);
Reducer(之所以这样称呼,是因为你会将函数类型传递给数组方法Array.prototype.reduce(reducer, initialValue)
)是源自 Redux 的一种模式。如果你不熟悉 Redux,简而言之,Reducer 是一个纯函数,它接受先前的状态和动作作为参数,并返回下一个状态。
(prevState, action) => newState
Actions 是描述发生了什么的信息,Reducer 根据这些信息指定状态应该如何变化。Actions 通过dispatch(action)
方法传递。
使用它的 3 个理由
大多数情况下,使用useState()
基于 的方法就能很好地解决问题useReducer()
。但在某些情况下,useReducer()
这种方法更可取。
下一个状态取决于前一个状态
当状态依赖于前一个状态时,使用此方法总是更好的。它将为您提供更可预测的状态转换。简单的例子如下:
function reducer(state, action) {
switch (action.type) {
case 'ADD': return { count: state.count + 1 };
case 'SUB': return { count: state.count - 1 };
default: return state;
}
}
function Counter() {
const [state, dispatch] = React.useReducer(reducer, { count: 0 });
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'ADD'})}>Add</button>
<button onClick={() => dispatch({type: 'SUB'})}>Substract</button>
</>
);
}
复杂状态形状
当状态包含多个原始值时,例如嵌套对象或数组。例如:
const [state, dispatch] = React.useReducer(
fetchUsersReducer,
{
users: [
{ name: 'John', subscribred: false },
{ name: 'Jane', subscribred: true },
],
loading: false,
error: false,
},
);
管理这个本地状态更容易,因为参数相互依赖,并且所有逻辑都可以封装到一个 reducer 中。
易于测试
Reducer 是纯函数,这意味着它们没有副作用,并且对于相同的参数必须返回相同的结果。由于它们不依赖于 React,因此测试起来更容易。让我们从计数器示例中取出一个 Reducer,并使用模拟状态进行测试:
test("increments the count by one", () => {
const newState = reducer({ count: 0 }, { type: "ADD" });
expect(newState.count).toBe(1)
})
结论
useReducer()
是一种替代方案useState()
,它能让您更好地控制状态管理,并简化测试。所有情况都可以用useState()
此方法完成,因此总而言之,请使用您熟悉的方法,并且它对您和同事来说更容易理解。