【发布时间】:2021-05-28 21:33:23
【问题描述】:
与useState 相比,我很难理解useReducer 何时以及为何具有优势。有很多争论,但对我来说,没有一个是有意义的,在这篇文章中,我试图将它们应用到一个简单的例子中。
也许我遗漏了一些东西,但我不明白为什么应该在 useState 之外的任何地方使用 useReducer。我希望你能帮助我澄清这一点。
我们来看这个例子:
版本 A - 带有 useState
function CounterControls(props) {
return (
<>
<button onClick={props.increment}>increment</button>
<button onClick={props.decrement}>decrement</button>
</>
);
}
export default function App() {
const [complexState, setComplexState] = useState({ nested: { deeply: 1 } });
function increment() {
setComplexState(state => {
// do very complex logic here that depends on previous complexState
state.nested.deeply += 1;
return { ...state };
});
}
function decrement() {
setComplexState(state => {
// do very complex logic here that depends on previous complexState
state.nested.deeply -= 1;
return { ...state };
});
}
return (
<div>
<h1>{complexState.nested.deeply}</h1>
<CounterControls increment={increment} decrement={decrement} />
</div>
);
}
看到这个stackblitz
版本 B - 带有 useReducer
import React from "react";
import { useReducer } from "react";
function CounterControls(props) {
return (
<>
<button onClick={() => props.dispatch({ type: "increment" })}>
increment
</button>
<button onClick={() => props.dispatch({ type: "decrement" })}>
decrement
</button>
</>
);
}
export default function App() {
const [complexState, dispatch] = useReducer(reducer, {
nested: { deeply: 1 }
});
function reducer(state, action) {
switch (action.type) {
case "increment":
state.nested.deeply += 1;
return { ...state };
case "decrement":
state.nested.deeply -= 1;
return { ...state };
default:
throw new Error();
}
}
return (
<div>
<h1>{complexState.nested.deeply}</h1>
<CounterControls dispatch={dispatch} />
</div>
);
}
看到这个stackblitz
在很多文章(包括docs)中,两个论点似乎很流行:
“useReducer 适用于复杂的状态逻辑”。 在我们的示例中,假设 complexState 很复杂,有许多修改操作,每个操作都有很多逻辑。 useReducer 在这里有什么帮助?对于复杂的状态,拥有单独的函数而不是拥有一个 200 行的 reducer 函数难道不是更好吗?
“如果下一个状态依赖于前一个状态,useReducer 就很好”。 我可以用 useState 做同样的事情,不是吗?直接写setState(oldstate => {...})
潜在的其他优势:
- “我不必传递多个函数,而只需传递一个 reducer”:好的,但我也可以使用 useCallback 等将我的函数包装到一个“动作”对象中。正如已经提到的,在不同的函数中有不同的逻辑对我来说似乎是件好事。
- “我可以为 reducer 提供一个上下文,这样我的复杂状态就可以在整个应用程序中轻松修改”。是的,但您也可以提供该上下文中的单个函数(可能由 useCallback 包装)
我看到的缺点:
- 单个超长函数中的多个不同操作似乎令人困惑
- 更容易出错,因为您必须检查 reducer 函数或依赖打字稿等来找出可以传递给 reducer 的字符串以及附带的参数。调用函数时,这要简单得多。
考虑到所有这些:你能给我一个很好的例子吗?useReducer 真的很出色,并且不能轻易地用useState 重写为一个版本?
【问题讨论】:
标签: reactjs react-hooks use-state use-reducer