前言
最近开始学习React,跟着Kent学,有很多干货,这里分享React Hook中的useReducer
很多时候并不需要useReducer,因为useReducer增加了代码的复杂度,但是如果计算逻辑比较复杂涉及到多个state的互动,useReducer就可以比较好的解决这类问题
代码分享到了codesandbox,App.js是最后的最佳实践,中间代码放在了archive文件夹上了
一、useReducer
1.0 useReducer的API
useReducer的签名如下
function useReducer<R extends ReducerWithoutAction<any>, I>(
reducer: R,
initializerArg: I,
initializer: (arg: I) => ReducerStateWithoutAction<R>
): [ReducerStateWithoutAction<R>, DispatchWithoutAction];
其中
type ReducerWithoutAction<S> = (prevState: S) => S;
type DispatchWithoutAction = () => void;
简单来说就是
const [state, dispatch] = useReducer(reducer, initValue)
举个例子,可以看出来useReducer跟useState是可以互换的
当setCount也就是dispatch被调用的时候,reducer中第二个参数newState就会把dispatch的内容传过来,比如dispatch(1),newState就是1
import * as React from 'react'
function countReducer(prevState, newState) {
return newState
}
function Counter({initialCount = 0, step = 1}) {
const [count, setCount] = React.useReducer(countReducer, initialCount)
const increment = () => setCount(count + step)
return <button onClick={increment}>{count}</button>
}
function App() {
return <Counter />
}
export default App
1.1 传递object
既然dispatch可以传递value,那么这个value也可以是object,不妨传递object试试,这样只要setState中传递的object就会成为state的新的object,这是因为countReducer返回的是 { …prevState, …newState.count }
function countReducer(prevState, newState) {
console.log(newState);
return { ...prevState, ...newState.count };
}
function Counter({ initialCount = 0, step = 1 }) {
const [state, setState] = React.useReducer(countReducer, {
count: initialCount
});
const { count } = state;
const increment = () => setState({ count: count + step });
return <button onClick={increment}>{count}</button>;
}
function App() {
return <Counter />;
}
export default App;
1.2 useReducer的最佳实践
可以发现上面的例子中,setState中有计算的逻辑,我们希望吧这个计算的逻辑从组建中抽离,放到一个专门的函数中处理,那么每次调用setState的时候告诉需要什么方法就行
const increment = () => dispatch({ type: "INCREMENT", step });
于是得到了, countReducer就是统一处理counter就算逻辑的,实现了组建跟计算分离
import * as React from "react";
function countReducer(state, action) {
const { type, step } = action;
switch (type) {
case "increment": {
return {
...state,
count: state.count + step
};
}
default: {
throw new Error(`Unsupported action type: ${action.type}`);
}
}
}
function Counter({ initialCount = 0, step = 1 }) {
const [state, dispatch] = React.useReducer(countReducer, {
count: initialCount
});
const { count } = state;
const increment = () => dispatch({ type: "INCREMENT", step });
return <button onClick={increment}>{count}</button>;
}
function App() {
return <Counter />;
}
export default App;
总结
这里主要一步步演进了useReducer,最后到最佳实践,可以看出来,useReducer在某种程度可以跟useState互换,如果组件state比较多,计算比较复杂,useReducer是一个不错的解决方案