前言:在最近的业务组件中,我完全使用useReducer代替了useState,并且因此感受到较大的开发快感,以至于我觉得所有关联性高的状态都应该用useReducer聚合在一起管理,应该尽可能地使用useReducer来代替useState。
why not useState?
我们先说下useState哪里不好。
以一个列表的展示为例,简单的发请求然后渲染列表,我们看看有哪些我们需要关心的state:
- isLoading: 当true时,我们要展示Loading组件
- listData: 当获取到后端返回的数据后,如果是空列表,展示NoData组件,如果是有值,则展示List组件渲染列表数据
- error: 当请求失败时获得,展示Error组件
如果看看每个state都用一个useState来创建代码会是怎样的:
const [loading, setLoading] = useState(false);
cosnt [listData, setListData] = useState([]);
const [error, setError] = useState(null);
const getListData = () => {
setLoading(true);
service.post(url, params).then(res => {
setListData(res.data.listData);
setLoading(false);
}).catch(err => {
setError(error);
setLoading(false);
})
}
这样的代码的问题在哪里呢?
这种代码,当代码执行到某个改变state的节点时,阅读者仅仅能确定某个state被set成了新值,但不明确这个/这些set动作具体的含义是什么,也不明确set之后组件整体是什么样子的。
比如我执行到catch中的的代码的时候,我知道你执行了setError(error); setLoading(false);
,但是我问你,两个set具体描述了一个什么动作,set之后整个组件是个什么状态?
对于这个例子,你数秒内就能说出来,这是show error的动作,set之后组件是一个展示Error子组件的状态。
然而你之所以可以这么快的说出来仅仅是因为这个例子太简单了,如果这里一坨if else, set了一坨state,你还有信心能这么快回答出这个问题吗?
理清状态的改变是读懂业务代码的关键所在,相信绝大多数人都浪费过时间在理清业务代码的状态上,并感受到不爽。
然而useReducer就能有效地帮助我们解决这个问题,书写出状态流转明确的代码。
how useReducer work?
在我的理解中useReducer这个Api最重要的两个作用就是:
- 给set动作起名,用一个动作表示多条状态的变更
- 给set之后的状态起名,用一个状态名表示多条状态的组合 让我们看看用useReducer的思路:
const reducer = (state, action) => {
switch (action.actionType) {
case 'REQUEST_SRART':
return {
...state,
stateType: 'LOADING',
loading: true
};
case 'REQUEST_SUCCESS':
return {
...state,
stateType: action.listData.length === 0 ? 'NO_DATA' : 'HAS_DATA',
loading: false,
listData: action.listData
};
case 'REQUEST_FAIL':
return {
...state,
stateType: 'ERROR',
loading: false,
error: action.error
}
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, {});
const getListData = () => {
dispatch({
actionType: 'REQUEST_SRART'
})
service.post(url, params).then(res => {
dispatch({
actionType: 'REQUEST_SUCCESS',
listData: res.data.listData
})
}).catch(err => {
dispatch({
actionType: 'REQUEST_FAIL',
error
})
})
}
通过useReducer,任何的状态变更的动作都通过actionType进行了命名,变更后的组件状态也有了stateType进行描述,这使得代码的可读性大大提升。
more about useReducer...
最后我还想分享一下我使用useReducer书写业务组件的一些相关经验。
通常来说,对于一个不过于复杂的功能,我们可以在这个功能模块的根组件中使用useReducer来产生整个组件需要的state和修改state的dispatch方法,我们首先通过书写reducer来理清整个组件的状态流转过程,然后再通过useEffect来写effect逻辑。
组件内产生并仅向下传递的状态就使用一个useReducer创建一个state就可以了,理想情况下子组件消费的都仅仅是这个state的衍生状态。
如果子组件也需要修改这个唯一state,那么就在父组件中把dispatch传到子组件供其调用来修改。
以上就是我这篇文章想表达的全部内容了,Thank you for reading~