其实一开始看到useReducer的时候,还以为它跟react的好伙伴redux有什么关系。然而也就是借鉴了redux的设计模式的状态操作工具而已。
目前使用Redux的小伙伴们可能还需要用原来的react-redux提供的connect HOC一段时间。尽管facebook已经在做相关的hook,目前还处于不稳定的状态,使用后果自负(项目地址:https://github.com/facebookincubator/
回到useReducer,它的存在是为一步操作更新多个状态设计。
举个不太恰当的拉数据的例子。通常拉数据的时候需要显示表示正在“加载中”的动画,加载数据出错则需要提示相关的错误,成功则显示数据。这里我们假设有2个状态值isLoading和 error,加载成功的数据则是由react-redux从props里传进来:
- 加载中:
isLoading = true; error = undefined; data = undefined | { ... };
- 成功:
isLoading = false; error = undefined; data = { ... };
- 失败:
isLoading = false; error = ‘Error message.’; data = { ... };
比较容易想到的做法是用useState,如下:
function MyComponent({ loadDataAction, data }) {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(undefined);
const loadData = useCallback(async () => {
setIsLoading(true);
setError(undefined);
try {
await loadDataAction();
} catch(err) {
setError(err);
} finally {
setIsLoading(false);
}
});
return (
<div>
<button onClick={loadData} disabled={isLoading}>Load Data</button>
{error ? (
<p>{error}</p>
) :
data ? (
<h3>Loaded data below</h3>
<div>{data}</div>
) : null
</div>
);
}
改成useReducer的话每种操作对状态造成什么影响会更加清晰,并且易于重用:
function loadStateReducer(state, action) {
switch (action.type) {
case ‘loading’:
return {
isLoading: true,
error: undefined
};
case ‘success’;
return {
isLoading: false,
error: undefined
};
case ‘error’:
return {
isLoading: false,
error: action.error
};
}
return state;
}
function MyComponent({ loadDataAction, data }) {
const [state, dispatch] = useReducer(loadStateReducer, {
isLoading: false,
error: undefined
});
const { isLoading, error } = state;
const loadData = useCallback(async () => {
dispatch({ type: ‘loading’ });
try {
await loadDataAction();
dispatch({ type: ‘success’ });
} catch(err) {
dispatch({ type: ‘error’, error: err });
}
});
return (
<div>
<button onClick={loadData} disabled={isLoading}>Load Data</button>
{error ? (
<p>{error}</p>
) :
data ? (
<h3>Loaded data below</h3>
<div>{data}</div>
) : null
</div>
);
}
讲真,用useState设置一个object state是等同的操作。所以用哪个看个人喜好吧。
这里要注意的是,当两个state值有相关性(比如B是根据A计算得到的结果),那就有必要考虑用object类型的state或者useReducer,否则容易遇到上下文不一致导致出现非自己期望的结果(遇到过一次,容我想到恰当的例子再谈这个问题)。
另外,我说这个例子不恰当是因为这个例子中没有在使用异步操作后考虑component实例是否还是挂载的状态,可能导致内存泄漏,并且也会有相关的报错。