所以,Hooks从React 16.8开始提供。从它们的文档中可以看出,Hooks是用于在函数组件中替代state的。基本的hooks有:useState
、useEffect
、useContext
,但还有其他一些额外的hooks,其中之一就是useReducer
,它似乎使用了与Redux相同的action-dispatch
架构。
问题是,因为类似,Hooks是否成为Redux的替代品?
它适合特定的项目吗?
它适用于哪些情况?
所以,Hooks从React 16.8开始提供。从它们的文档中可以看出,Hooks是用于在函数组件中替代state的。基本的hooks有:useState
、useEffect
、useContext
,但还有其他一些额外的hooks,其中之一就是useReducer
,它似乎使用了与Redux相同的action-dispatch
架构。
问题是,因为类似,Hooks是否成为Redux的替代品?
它适合特定的项目吗?
它适用于哪些情况?
Redux是一个鼓励特定数据流程的库。
另一方面,react-redux
实现了 React 友好的方法并提供了许多中间件和包装器,以便库使用者不必自己设置整个过程。
虽然 useReducer
是 Redux 工作中的一部分,但它并不是完整的 Redux。为了能够在组件的深层使用 dispatch 和 state,您仍然需要结合使用 useContext
和 useReducer
,这就像重新发明轮子。
此外,useReducer
只提供了一个 dispatch
方法,您可以使用它来分派纯对象作为操作。目前还没有办法添加像 thunk
、saga
等中间件。
您还可以在应用程序中使用多个 reducers,但是将它们组合成一个单一的 store 的方式仍然需要由开发人员处理。
React 文档 还指出,当状态逻辑复杂时,useReducer
是替代 useState
的选择。
像
useReducer
通常比useState
更可取,当您有涉及多个子值的复杂状态逻辑或下一个状态与先前状态无关时。
useContext
、useReducer
这样的钩子可以消除小型应用程序对Redux
的依赖关系。此外,useReducer
还让您可以传递分派而不是回调函数,以优化触发深度更新的组件性能。useReducer
只是提供了一个 dispatch
方法,您可以使用它来分派普通的对象作为操作。目前还没有办法添加 middlewares
,例如 thunk
、saga
等等。- 谢谢 - sledgeweightuseReducer
有什么好的使用案例吗?乍一看,它似乎有点过于复杂,仅用于本地状态。 - Robo Robok因此,如果要比较Redux和useReducer
Redux:
useReducer:
useReducer
是否可以替代Redux?useReducer
的纯React解决方案进行比较:
(灵感来自于this article)import { useReducer, useContext, useMemo } from "react"
import { rootReducer } from "./reducers"
const GlobalContext = React.createContext()
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(rootReducer, { count: 0 });
const store = useMemo(() => [state, dispatch], [state]);
// You can also separate dispatch and state context providers
return (
<GlobalContext.Provider value={store}>{children}</GlobalContext.Provider>
);
};
// Provider is placed at top-level to emulate a global state store
ReactDOM.render(<Provider> <App /> </Provider>, document.getElementById('root'))
const Comp = () => {
// extract this UI logic in its own custom hook for better encapsulation
const [state, dispatch] = useContext(GlobalContext);
// ...
};
Redux store可以灵活地实例化(createStore
)和访问。在原生React中,store绑定到UI中的单个useReducer
。我们可以通过上下文或props将其状态向下传递。
与Redux一样,完全相同的纯rootReducer
函数用于原生React。
redux-thunk
和redux-saga
是Redux中最流行的两个middlewares,用于异步操作和副作用。使用useReducer
时,我们没有任何内置的中间件API。相反,我们首先进行异步处理,然后将结果转发给dispatch
:
const [state, dispatch] = useContext(GlobalContext);
// ...
<button onClick={() => dispatchAsync(dispatch)}> Process </button>
const dispatchAsync = dispatch => {
fetchData().then(data => dispatch({type: "increment"}, data))
};
只要现有的Redux中间件涵盖了公共API,就仍然可以将其与useReducer
集成 - 您可以查看这个答案获取更多信息。
useReducer
没有直接的集成方式,因此您可能会错过一个重要的工作流工具。 reinspect库使用Redux DevTools来检查useState
和useReducer
(尚未经过测试)。
useReducer
的使用始于本地状态和组件范围。正如您所见,它也可以提升到全局范围,接管大部分 Redux 的角色。useReducer
甚至可以提供更多的灵活性,因为全局状态可以在多个上下文之间划分。例如:在不同的状态树/上下文中分离低优先级和高优先级状态更改。combineReducers
这样的函数可以很容易地重新实现为 useReducer
,就像 这里 所示。const GlobalContext = React.createContext();
const Provider = ({ children }) => {
const [state, dispatch] = useReducer(rootReducer, { count: 0 });
const store = useMemo(() => [state, dispatch], [state]);
// You can also separate dispatch and state context providers
return (
<GlobalContext.Provider value={store}>{children}</GlobalContext.Provider>
);
};
const rootReducer = (state, action) =>
action === "increment" ? { count: state.count + 1 } : state;
const dispatchAsync = dispatch => {
// just do the async operation before and invoke dispatch afterwards
setTimeout(() => dispatch("increment"), 1000);
};
const Comp = () => {
// You can extract this UI logic in its own custom hook for better encapsulation
const [state, dispatch] = useContext(GlobalContext);
return (
<div>
<div>Counter: {state.count}</div>
<button onClick={() => dispatchAsync(dispatch)}>
Increment async (1sec delay)
</button>
</div>
);
};
ReactDOM.render(<Provider><Comp /></Provider>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script>
<div id="root"></div>
<script>var { useReducer, useContext, useMemo } = React</script>
Provider
内部有一个稳定的children
引用,因此它永远不会在上下文值更改时重新渲染(还假设Provider
放置在组件层次结构的顶部/根部以进行无/少量更新)。虽然你是对的,在更大的应用程序中,性能是应该在这些概念之间仔细评估的一个因素。 - ford04useReducer
的状态是局限于单个组件的 - 如果你想在整个应用程序中使用这个状态,你需要通过props将其 (或者 dispatch
函数) 传递下去。实际上,它只是更有结构的 useState
的一个版本 - 事实上,useState
在幕后使用了 useReducer
!
另一方面,Redux 做了更多的事情 - 其中之一就是通过上下文(Context)使状态在整个应用程序中可用,并提供 API 将您深度嵌套的组件连接到此状态,而无需传递props。
换句话说:
useReducer
提供了结构化的本地状态更新。如果你想用 Hooks 来实现自己的 Redux,你需要使用一些组合使用 useReducer
和 useContext
的方法。