其實一開始看到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實例是否還是掛載的狀態,可能導致內存泄漏,並且也會有相關的報錯。