Rap*_*ael 5 reactjs react-hooks use-reducer use-state
我正在努力理解useReducer与useState. 有很多争论,但对我来说,没有一个真正有意义,在这篇文章中,我试图将它们应用到一个简单的例子中。
也许我错过了一些东西,但我真的不明白为什么useReducer应该在任何地方使用 over 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>
);
}
Run Code Online (Sandbox Code Playgroud)
看到这个堆栈闪电战
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>
);
}
Run Code Online (Sandbox Code Playgroud)
看到这个堆栈闪电战
在很多文章(包括docs)中,两个论点似乎非常流行:
“useReducer 适用于复杂的状态逻辑”。在我们的示例中,假设complexState真的很复杂,我们有许多修改操作,每个操作都有很多逻辑。如何useReducer帮助在这里?对于复杂的状态,拥有单独的功能而不是拥有单个 200 行的减速器功能不是更好吗?
“如果下一个状态取决于前一个状态,则 useReducer 是好的”。我可以用 useState 做同样的事情,不是吗?简单地写setState(oldstate => {...})
潜在的其他优势:
我看到的缺点:
考虑到所有这些:你能给我一个很好的例子,其中useReducer真正闪耀并且不能轻易重写为带有 的版本useState?
Rap*_*ael 16
几个月后,我觉得我必须对这个主题添加一些见解。如果在 和 之间进行选择useReducer只是useState个人喜好的问题,为什么人们会写这样的东西:
\n\n\nuseReducer 确实是 Hooks 的作弊模式。一开始您可能不喜欢它,但它避免了依赖 useState 的类和组件中出现的大量潜在问题。了解使用Reducer。
\n
\n\n当您具有涉及多个子值的复杂状态逻辑或当下一个状态依赖于前一个状态时,useReducer 通常优于 useState。useReducer 还可以让您优化触发深度更新的组件的性能,因为您可以向下传递调度而不是回调。
\n
反应文档:
\n\n\n我们建议在上下文中传递调度,而不是在 props 中传递单独的回调。
\n
因此,让我们尝试将其确定下来并找到一个useReducer清晰可见的场景useState:
VersionA 的方法(useState&传递回调)可能存在问题:
App!的每次渲染中。useCallback函数会有所帮助,但这种模式很快就会变得乏味,特别是当我们需要另外调用useMemo一个actions对象时。(而且我不是这方面的专家,但从性能角度来看,这听起来不太令人信服)useCallback不会有太大帮助。如果我们使用减速器:
\ndispatch函数始终具有稳定的身份!(参见反应文档)再次参见 Dan Abramov 的Twitter 帖子:
\n\n\n并且 \xe2\x80\x9cdispatch\xe2\x80\x9d 身份始终稳定,即使减速器是内联的。因此,您可以依靠它进行性能优化,并将调度作为静态值免费传递给上下文。
\n
useReducer在此代码中,我尝试强调使用我之前尝试描述的一些优点:
import React, { useEffect } from "react";\nimport { useState, useReducer } from "react";\n\nfunction MyControls({ dispatch }) {\n // Cool, effect won\'t be called if reducer function changes.\n // dispatch is stable!\n // And still the up-to-date reducer will be used if we call it\n useEffect(() => {\n function onResize() {\n dispatch({ type: "set", text: "Resize" });\n }\n\n window.addEventListener("resize", onResize);\n return () => window.removeEventListener("resize", onResize);\n }, [dispatch]);\n\n return (\n <>\n <button onClick={() => dispatch({ type: "set", text: "ABC" })}>\n Set to "ABC"\n </button>\n <button onClick={() => dispatch({ type: "setToGlobalState" })}>\n Set to globalAppState\n </button>\n <div>Resize to set to "Resized"</div>\n </>\n );\n}\n\nfunction MyComponent(props) {\n const [headlineText, dispatch] = useReducer(reducer, "ABC");\n\n function reducer(state, action) {\n switch (action.type) {\n case "set":\n return action.text;\n case "setToGlobalState":\n // Cool, we can simply access props here. No dependencies\n // useCallbacks etc.\n return props.globalAppState;\n default:\n throw new Error();\n }\n }\n\n return (\n <div>\n <h1>{headlineText}</h1>\n <MyControls dispatch={dispatch} />\n </div>\n );\n}\n\nexport default function App() {\n const [globalAppState, setGlobalAppState] = useState("");\n\n return (\n <div>\n global app state:{" "}\n <input\n value={globalAppState}\n onChange={(e) => setGlobalAppState(e.target.value)}\n />\n <MyComponent globalAppState={globalAppState} />\n </div>\n );\n}\nRun Code Online (Sandbox Code Playgroud)\n请参阅此代码和框
\ndispatch\的身份保持不变!它不会触发效果| 归档时间: |
|
| 查看次数: |
213 次 |
| 最近记录: |