一、引言
在现代前端开发中,React 凭借其组件化和声明式编程的特性,成为了构建用户界面的主流选择。然而,随着应用复杂度的提升,如何高效、可维护地管理组件状态,成为了开发者面临的核心挑战之一。React 提供了多种状态管理方案,从组件内部的 useState 到跨组件层级的 Context API,再到更高级的 useReducer Hook,每种方案都有其独特的适用场景和优势。
本文将聚焦于 React 中两个强大且常被结合使用的 Hook:useReducer 和 useContext。useReducer 提供了比 useState 更强大的状态逻辑管理能力,尤其适用于复杂状态的更新逻辑;而 useContext 则完美解决了组件之间跨层级数据传递的“Prop Drilling”问题,使得全局状态的共享变得轻而易举。当二者结合使用时,它们能够构建出一种清晰、高效且易于维护的全局响应式状态管理模式,足以应对大多数中大型应用的需求。
我们的目标是,不仅要深入剖析 useReducer 和 useContext 的基本用法和核心概念,更要从底层原理出发,揭示它们在 React 内部是如何工作的。同时,我们将结合您提供的实际代码示例(一个简单的 Todo 应用),详细展示如何在实践中有效地运用这两个 Hook,并最终实现一个全局应用级别的状态管理方案。通过本文,您将能够全面理解 useReducer 和 useContext 的精髓,并将其灵活应用于您的 React 项目中,写出更健壮、更优雅的代码。
二、useReducer 深度解析
2.1 什么是 useReducer?
useReducer 是 React 提供的一个 Hook,它允许我们使用 Reducer 模式来管理组件的状态。简单来说,它接收一个 reducer 函数和一个初始状态 initialState,并返回当前的 state 以及一个 dispatch 函数。其函数签名通常如下所示:
const [state, dispatch] = useReducer(reducer, initialState, init?);
其中:
reducer:一个纯函数,它接收当前的state和一个action对象,并返回一个新的state。这是useReducer的核心,它定义了状态如何根据不同的action进行更新。initialState:组件的初始状态值。init?(可选):一个惰性初始化函数,如果提供,initialState将作为其参数,函数的返回值作为最终的初始状态。这在初始状态计算成本较高时非常有用。state:由reducer函数维护的当前状态。dispatch:一个函数,用于派发action。当调用dispatch函数并传入一个action对象时,React 会将当前的state和这个action传递给reducer函数,然后用reducer返回的新状态来更新组件。
useReducer 与 useState 都是用于管理组件状态的 Hook,但它们各有侧重。useState 适用于管理简单的状态,例如布尔值、字符串或数字。而 useReducer 则更适合管理复杂的状态逻辑,特别是当状态的更新依赖于之前的状态,或者状态更新逻辑比较复杂且涉及多个子状态时。它将状态更新的逻辑从组件中分离出来,使得组件更加专注于 UI 的渲染,从而提高了代码的可读性和可维护性。
2.2 useReducer 的核心概念
理解 useReducer,需要掌握其背后的几个核心概念:Reducer 纯函数、初始状态和派发动作。
Reducer 纯函数:状态的生产者与规则制定者
Reducer 是 useReducer 的灵魂。它是一个纯函数,其职责是根据当前的 state 和接收到的 action 来计算并返回一个新的 state。纯函数有以下两个重要特性:
- 确定性 (Determinism) :给定相同的输入,总是返回相同的输出。这意味着 Reducer 函数不应该依赖于外部作用域的变量,也不应该有随机性。
- 无副作用 (No Side Effects) :除了返回新的状态外,Reducer 函数不应该修改任何外部数据,不应该执行网络请求、DOM 操作等副作用。它只负责状态的计算。
在您提供的 todoReducer.js 文件中,我们可以清晰地看到一个典型的 Reducer 纯函数实现:
// todoReducer.js
function todoReducer(state, action){
switch(action.type){
case 'ADD_TODO':
return [...state, {
id: Date.now(),
text:action.text,
done:false
}];
case 'REMOVE_TODO':
return state.filter(todo => todo.id !== action.id);
case 'TOGGLE_TODO':
return state.map(todo => todo.id === action.id ? {...todo, done:!todo.done} : todo);
default:
return state;
}
}
export default todoReducer;
这个 todoReducer 函数接收 state (一个待办事项数组) 和 action 对象。根据 action.type 的不同,它会执行不同的操作:
ADD_TODO:添加一个新的待办事项。这里使用了展开运算符...state来创建一个新的数组,而不是直接修改原有的state数组。这是实现状态不可变性 (Immutability) 的关键。REMOVE_TODO:移除一个待办事项。同样,filter方法会返回一个新的数组,保持了原状态的不可变性。TOGGLE_TODO:切换待办事项的完成状态。map方法也返回一个新的数组,并且只修改了匹配id的待办事项的done属性,其他待办事项保持不变。default:如果action.type不匹配任何已知的类型,则返回当前的state。这是非常重要的,它确保了在遇到未知action时状态不会被意外修改。
状态的不可变性是 Reducer 模式中一个至关重要的概念。React 依赖于状态的引用地址变化来判断是否需要重新渲染组件。如果直接修改了原有的 state 对象或数组,React 可能无法检测到状态的变化,从而导致组件不更新。因此,在 Reducer 中,我们总是应该返回一个新的状态对象或数组,而不是修改旧的。这不仅有助于 React 的渲染机制,也使得状态的变化更易于追踪和调试。
Initial State (初始状态):状态的起点
initialState 是 useReducer Hook 的第二个参数,它定义了组件状态的初始值。在组件首次渲染时,useReducer 会使用这个 initialState 来初始化其内部状态。例如,在 useTodos.js 中,useReducer 的 initialState 是通过 inital 参数传入的,默认值为空数组 []:
// useTodos.js
export function useTodos(inital=[]){
const [todos, dispatch] = useReducer(todoReducer, inital);
// ...
}
这意味着如果 useTodos 在调用时没有传入 inital 参数,那么 todos 的初始值将是一个空数组。
Dispatch (派发动作):触发状态更新的唯一途径
dispatch 函数是 useReducer 返回的第二个元素,它是触发状态更新的唯一途径。当我们需要改变状态时,我们不会直接修改 state,而是调用 dispatch 函数并传入一个 action 对象。这个 action 对象通常是一个普通的 JavaScript 对象,它至少包含一个 type 属性,用于描述要执行的动作类型,还可以包含一个 payload 属性或其他数据,用于携带更新状态所需的信息。
例如,在 useTodos.js 中,addTodo、removeTodo 和 toggleTodo 函数都通过调用 dispatch 来触发状态更新:
// useTodos.js
// ...
const addTodo = (text)=>dispatch({type:'ADD_TODO',text})
const removeTodo = (id)=>dispatch({type:'REMOVE_TODO',id})
const toggleTodo = (id)=>dispatch({type:'TOGGLE_TODO',id})
// ...
当 dispatch 被调用时,React 会将当前的 state 和传入的 action 对象传递给 reducer 函数。reducer 函数计算出新的 state 后,React 会用这个新状态来更新组件,并触发组件的重新渲染。
2.3 useReducer 的底层原理
要深入理解 useReducer,我们需要对其在 React 内部的工作机制有所了解。React 的 Hook 机制是基于链表实现的,每个 Hook 都有一个内部状态。当组件首次渲染时,useReducer 会初始化其内部状态并将其与组件实例关联起来。当 dispatch 函数被调用时,它会触发 React 的调度机制。
- 调度更新:
dispatch调用会告诉 React 有一个状态更新请求。React 会将这个更新请求放入一个更新队列中。 - 执行 Reducer:在下一个渲染周期中,React 会从更新队列中取出待处理的
action,并使用当前的state和action调用reducer函数。reducer函数执行后,会返回一个新的state。 - 状态更新与比较:React 会将
reducer返回的新state更新到useReducer内部的状态中。然后,React 会比较新旧state的引用地址。如果引用地址发生了变化(即reducer返回了一个新的对象或数组),React 就会标记该组件需要重新渲染。 - 组件重新渲染:被标记的组件及其子组件会进行重新渲染。在重新渲染过程中,
useReducer会返回最新的state。
useReducer 的设计哲学与 Redux 等状态管理库非常相似。实际上,useReducer 可以看作是 Redux 在 React Hook 中的一个简化版本。它们都遵循“单一数据源”和“状态不可变性”的原则,通过纯函数来管理状态的更新。useReducer 提供了 Redux 的核心思想,但没有 Redux 那么多的概念和中间件,更轻量级,适合在 React 应用内部进行局部或中等复杂度的状态管理。
2.4 useReducer 的实践应用
在实际项目中,我们通常会将 useReducer 封装成自定义 Hook,以便更好地复用状态逻辑。您提供的 useTodos.js 就是一个很好的例子:
// useTodos.js
import {
useReducer,
} from 'react'
import todoReducer from '../reducers/todoReducer';
export function useTodos(inital=[]){
const [todos, dispatch] = useReducer(todoReducer, inital);
const addTodo = (text)=>dispatch({type:'ADD_TODO',text})
const removeTodo = (id)=>dispatch({type:'REMOVE_TODO',id})
const toggleTodo = (id)=>dispatch({type:'TOGGLE_TODO',id})
return {
todos,
addTodo,
removeTodo,
toggleTodo,
}
}
这个 useTodos 自定义 Hook 封装了 Todo 应用的所有状态管理逻辑:
- 它内部使用了
useReducer来管理todos数组的状态,并传入了todoReducer。 - 它暴露了
todos状态以及addTodo、removeTodo、toggleTodo等操作函数。这些操作函数内部都调用了dispatch来触发相应的action。
通过这种方式,任何需要管理 Todo 列表的组件都可以直接调用 useTodos Hook,而无需关心 useReducer 的具体实现细节和 todoReducer 的逻辑。这大大提高了代码的复用性和可维护性。useReducer 在以下场景中特别有用:
- 复杂状态逻辑:当状态更新逻辑涉及多个子状态或依赖于前一个状态时,
useReducer可以使逻辑更清晰。 - 状态更新的集中管理:将所有状态更新逻辑集中在一个 Reducer 函数中,便于统一管理和调试。
- 性能优化:当
dispatch函数作为 prop 传递给子组件时,由于dispatch函数在组件生命周期内是稳定的,不会因为父组件的重新渲染而重新创建,这有助于避免不必要的子组件渲染。
三、useContext 深度解析
3.1 什么是 useContext?
useContext 是 React 提供的另一个核心 Hook,它允许函数组件订阅 React Context 的值。它的主要目的是解决 React 应用中常见的“Prop Drilling”(属性逐层传递)问题。当一个组件需要访问一个深层嵌套的子组件所需的数据时,如果数据是通过 props 从父组件一层一层传递下去的,那么中间的组件即使不需要这些数据,也必须接收并传递它们,这会导致代码变得冗长、难以维护。
useContext 提供了一种在组件树中共享数据的方式,而无需通过 props 手动地在每一层传递。它使得组件可以从组件树中直接读取 Context 的值,从而简化了组件之间的通信,特别是在处理全局数据(如用户认证信息、主题设置、语言偏好等)时显得尤为强大。
3.2 useContext 的核心概念
理解 useContext,需要掌握 Context 对象、Provider 和 Consumer 这三个核心概念。
Context 对象:createContext 的作用
在使用 useContext 之前,我们首先需要创建一个 Context 对象。这通过 React.createContext() 方法实现。createContext 返回一个 Context 对象,它包含两个 React 组件:Provider 和 Consumer。在您提供的 TodoContext.js 文件中,我们看到了 TodoContext 的创建:
// TodoContext.js
import{
createContext,
}from 'react';
// 上下文
export const TodoContext = createContext(null);
createContext(null) 中的 null 是 Context 的默认值。这个默认值只在两种情况下使用:
- 当组件在没有匹配的
Context.Provider的情况下尝试读取 Context 值时。 - 当你在测试组件时,不希望渲染
Provider。
在实际应用中,通常会通过 Provider 提供一个非 null 的值。
Provider (提供者):Context.Provider 的作用与使用
Context.Provider 是 Context 对象上的一个组件,它的作用是向其子组件树中提供 Context 的值。任何在 Provider 组件内部渲染的组件,无论其嵌套深度如何,都可以访问到 Provider 提供的 Context 值。Provider 接收一个 value prop,这个 value 就是要共享给子组件的数据。
在您提供的 App.jsx 文件中,TodoContext.Provider 被用来共享 todosHook 返回的状态和方法:
// App.jsx
import { TodoContext } from './TodoContext'
import { useTodos } from './hooks/useTodos'
// ...
function App() {
const todosHook = useTodos();
return (
// App 状态管理
<TodoContext.Provider value ={todosHook}>
<h1>Todo App</h1>
<AddTodo />
<TodoList />
</TodoContext.Provider>
)
}
// ...
这里,useTodos() 返回了一个包含 todos 数组以及 addTodo、removeTodo、toggleTodo 等方法的对象。这个对象被作为 TodoContext.Provider 的 value 传递下去。这意味着 AddTodo 和 TodoList 组件,以及它们的所有子组件,都可以访问到 todos 状态和这些操作方法,而无需通过 props 一层层传递。
value 属性的重要性在于,每当 Provider 的 value 发生变化时,所有订阅了该 Context 的子组件都会重新渲染。因此,传递给 value 的对象引用稳定性非常重要。如果 value 每次渲染都创建新的对象引用,即使内容没有变化,也会导致子组件不必要的重新渲染,从而影响性能。在 App.jsx 中,todosHook 是 useTodos 自定义 Hook 的返回值,它返回的是一个对象。如果 useTodos 内部的 useReducer 状态发生变化,todosHook 的引用会更新,从而触发 Provider 下的组件重新渲染。
Consumer (消费者):useContext Hook 的作用与使用
useContext 是一个 Hook,它允许函数组件直接从最近的 Context.Provider 中读取 Context 的值。它接收一个 Context 对象(即 createContext 的返回值)作为参数,并返回该 Context 的当前值。
在您提供的 useTodoContext.js 文件中,useContext 被封装成了一个自定义 Hook:
// useTodoContext.js
import{
useContext
} from 'react'
import { TodoContext } from "../TodoContext";
export function useTodoContext(){
return useContext(TodoContext);
}
这个 useTodoContext Hook 使得在组件中消费 TodoContext 变得更加简洁和语义化。任何需要访问 Todo 状态和操作方法的组件,只需调用 useTodoContext() 即可。
例如,在 AddTodo.jsx 和 TodoList.jsx 中,它们都通过 useTodoContext 来消费共享状态:
// AddTodo.jsx
import { useTodoContext } from '../hooks/useTodoContext'
// ...
const AddTodo = () => {
// ...
const { addTodo } = useTodoContext(); // 消费 addTodo 方法
// ...
}
// ...
// TodoList.jsx
import { useTodoContext } from '../hooks/useTodoContext'
// ...
const TodoList = () => {
const {
todos,
toggleTodo,
removeTodo
} = useTodoContext(); // 消费 todos 状态和操作方法
// ...
}
// ...
通过 useContext,AddTodo 和 TodoList 组件能够直接获取到 TodoContext.Provider 提供的 todos 状态和相关的操作方法,而无需通过 props 从 App 组件层层传递,极大地简化了组件间的通信。
3.3 useContext 的底层原理
useContext 的底层实现涉及到 React 的内部协调(Reconciliation)过程和组件树的遍历。当一个组件调用 useContext(SomeContext) 时,React 会向上遍历组件树,寻找最近的 SomeContext.Provider。一旦找到,它就会使用该 Provider 的 value 作为 useContext 的返回值。
当 Context.Provider 的 value 发生变化时,React 会执行以下步骤:
- 标记订阅者:React 会识别所有订阅了该 Context 的组件(即那些调用了
useContext(SomeContext)的组件)。 - 触发重新渲染:所有被标记的订阅者组件都会被安排重新渲染。即使这些组件使用了
React.memo或shouldComponentUpdate进行性能优化,只要 Context 的value发生变化,它们也会被强制重新渲染。 - 更新 Context 值:在重新渲染过程中,
useContext会返回Provider最新的value。
性能考量:useContext 的一个重要性能考量是,当 Provider 的 value 发生变化时,所有消费该 Context 的组件都会重新渲染,即使它们只使用了 value 中的一小部分数据。这可能导致不必要的渲染。为了优化性能,可以采取以下策略:
- 拆分 Context:如果一个 Context 提供了多种不相关的数据,可以考虑将其拆分为多个更小的 Context,每个 Context 只提供一部分数据。这样,当其中一个 Context 的值变化时,只会重新渲染订阅了该特定 Context 的组件。
- 使用
useMemo稳定value:如果Provider的value是一个对象或数组,并且其内容在多次渲染之间可能不变,可以使用useMemo来缓存value对象,确保其引用地址的稳定性,从而避免不必要的子组件渲染。
3.4 useContext 的实践应用
useContext 在 React 应用中有着广泛的应用场景,尤其适用于管理那些需要在整个应用中共享的全局状态:
- 全局主题管理:例如,一个应用可能支持亮色和暗色主题。可以将当前主题信息存储在一个 Context 中,并在应用根部提供
Provider。所有需要根据主题调整样式的组件都可以通过useContext来获取当前主题。 - 用户认证状态管理:用户的登录状态、用户信息、权限等,通常需要在多个组件中访问。将这些信息放入 Context 中,可以方便地在任何需要的地方获取。
- 多语言切换:应用的语言设置也可以通过 Context 来管理。当用户切换语言时,更新 Context 的值,所有依赖语言的组件都会自动更新其显示内容。
- 配置信息:例如 API 地址、应用版本号等,这些不经常变化但需要在应用中多处使用的配置信息,也可以通过 Context 进行共享。
useContext 提供了一种简洁、高效的方式来解决组件间的数据共享问题,使得 React 应用的架构更加清晰和可维护。
四、useReducer 与 useContext 的强强联合
4.1 为什么需要结合使用?
useReducer 和 useContext 各自解决了 React 状态管理中的不同问题:useReducer 专注于复杂状态逻辑的集中管理和更新,而 useContext 则擅长解决跨组件层级的数据传递问题。当它们结合使用时,便能发挥出强大的协同效应,共同构建出一种优雅且高效的全局状态管理模式。
想象一下,在一个大型应用中,你可能有一个复杂的全局状态,例如用户会话信息、购物车数据、或者像我们 Todo 应用中的待办事项列表。这些状态的更新逻辑可能非常复杂,涉及多种操作和状态转换。同时,这些状态又需要在应用的多个组件中被访问和修改,而这些组件可能分布在组件树的不同层级。
useReducer负责复杂状态逻辑的管理:它将所有与状态更新相关的逻辑封装在一个纯粹的 Reducer 函数中,使得状态的变更可预测、可测试。通过dispatch机制,我们可以清晰地知道是哪个“动作”触发了状态的改变,以及状态是如何响应这个动作的。useContext负责状态的跨组件传递:它提供了一种“订阅-发布”的机制,允许任何嵌套的子组件无需通过 props 逐层传递,就能直接访问到由Provider提供的共享状态和操作方法。这极大地简化了组件间的通信路径,避免了冗余的 prop 传递。
因此,将 useReducer 和 useContext 结合起来,就能够实现一个全局应用级别的响应式状态管理。useReducer 负责“生产”和“管理”状态,而 useContext 则负责将这些“生产”出来的状态“分发”给需要它们的组件。这种模式既保证了状态更新逻辑的清晰和集中,又解决了状态在组件树中高效传递的问题。
4.2 结合使用的优势
这种组合模式带来了多方面的优势:
- 清晰的状态管理模式:状态的定义、初始值、更新逻辑(Reducer)以及触发更新的方式(Action)都集中管理,使得整个状态流向清晰可见。组件只需要关心如何派发动作和如何消费状态,而无需关心状态的具体管理细节。
- 避免 Prop Drilling:这是
useContext的核心优势。通过 Context,深层嵌套的组件可以直接访问到全局状态,无需通过中间组件传递不必要的 props,从而减少了代码的耦合度,提高了可读性和可维护性。 - 提高代码可维护性与复用性:将复杂的状态逻辑封装在自定义 Hook(如
useTodos)中,并通过 Context 共享,使得这些逻辑可以在应用的不同部分复用。当状态逻辑需要修改时,只需要修改一处(Reducer 或自定义 Hook),而无需修改多个组件。 - 性能优化潜力:虽然
useContext在value变化时会重新渲染所有订阅者,但结合useReducer时,dispatch函数本身是稳定的,不会在每次渲染时重新创建。这意味着你可以安全地将dispatch函数传递给子组件,而不会导致子组件不必要的重新渲染。对于value中包含的状态数据,可以通过useMemo等方式进一步优化,确保只有在真正需要时才触发重新渲染。
4.3 结合使用的实践
让我们再次审视您提供的 Todo 应用示例,它完美地展示了 useReducer 和 useContext 的结合使用:
-
useTodos封装useReducer逻辑: 在useTodos.js中,我们定义了一个自定义 Hook,它内部使用了useReducer来管理todos数组的状态。todoReducer负责定义ADD_TODO、REMOVE_TODO、TOGGLE_TODO等操作的具体逻辑。useTodos返回了todos状态以及addTodo、removeTodo、toggleTodo等便捷的操作函数。// useTodos.js import { useReducer } from 'react'; import todoReducer from '../reducers/todoReducer'; export function useTodos(initial = []) { const [todos, dispatch] = useReducer(todoReducer, initial); const addTodo = (text) => dispatch({ type: 'ADD_TODO', text }); const removeTodo = (id) => dispatch({ type: 'REMOVE_TODO', id }); const toggleTodo = (id) => dispatch({ type: 'TOGGLE_TODO', id }); return { todos, addTodo, removeTodo, toggleTodo, }; } -
TodoContext.Provider共享useTodos返回的状态和方法: 在App.jsx中,我们首先调用useTodos()来获取todos状态和相关的操作方法。然后,我们将这个todosHook对象作为TodoContext.Provider的value传递下去。这样,App组件的整个子组件树都可以访问到这些状态和方法。// App.jsx import { TodoContext } from './TodoContext'; import { useTodos } from './hooks/useTodos'; import AddTodo from './components/AddTodo'; import TodoList from './components/TodoList'; function App() { const todosHook = useTodos(); return ( <TodoContext.Provider value={todosHook}> <h1>Todo App</h1> <AddTodo /> <TodoList /> </TodoContext.Provider> ); } export default App; -
子组件通过
useTodoContext消费状态和方法: 在AddTodo.jsx和TodoList.jsx中,我们通过自定义的useTodoContextHook 来获取TodoContext中共享的值。这个useTodoContext内部简单地调用了useContext(TodoContext)。// useTodoContext.js import { useContext } from 'react'; import { TodoContext } from "../TodoContext"; export function useTodoContext(){ return useContext(TodoContext); }然后,
AddTodo组件可以轻松地获取addTodo方法来添加待办事项:// AddTodo.jsx import { useTodoContext } from '../hooks/useTodoContext'; // ... const AddTodo = () => { const { addTodo } = useTodoContext(); // ... };而
TodoList组件则可以获取todos数组以及toggleTodo和removeTodo方法来渲染列表和处理交互:// TodoList.jsx import { useTodoContext } from '../hooks/useTodoContext'; // ... const TodoList = () => { const { todos, toggleTodo, removeTodo } = useTodoContext(); // ... };
通过这个示例,我们可以清楚地看到 useReducer 和 useContext 如何协同工作,共同管理着 Todo 应用的全局状态。这种模式使得状态逻辑与 UI 渲染分离,组件之间通过 Context 进行通信,从而构建了一个清晰、可维护且易于扩展的 React 应用。
五、总结
通过本文的深入探讨,我们全面解析了 React 中 useReducer 和 useContext 这两个强大的 Hook。它们各自在 React 状态管理中扮演着不可或缺的角色,并在结合使用时展现出卓越的效能。
useReducer:它提供了一种结构化、可预测的方式来管理复杂的状态逻辑。通过将状态更新的规则封装在纯粹的 Reducer 函数中,我们能够清晰地定义状态如何响应不同的动作,并确保状态的不可变性。这使得状态管理更加健壮,易于测试和调试,尤其适用于状态转换路径复杂或依赖于先前状态的场景。useContext:它彻底解决了 React 应用中长期存在的“Prop Drilling”问题,为跨组件层级的数据共享提供了一条优雅的途径。通过Context.Provider和useContextHook,我们可以轻松地将全局或局部状态传递给组件树中的任何消费者,极大地简化了组件间的通信,并提高了代码的可读性和可维护性。
当 useReducer 和 useContext 强强联合时,它们共同构建了一个强大而灵活的全局响应式状态管理方案。useReducer 负责处理状态的复杂逻辑,而 useContext 则负责将这些经过精心管理的状态高效地分发到需要它们的组件。这种模式不仅使得状态管理逻辑与 UI 渲染逻辑分离,还促进了代码的模块化和复用,为构建可扩展、可维护的 React 大型应用奠定了坚实的基础。理解这些底层原理,将有助于我们更好地选择和使用适合自己项目的状态管理方案,并能够更深入地理解 React 生态系统的发展方向。
掌握 useReducer 和 useContext,意味着你掌握了 React 状态管理的精髓。它们将是你构建高性能、高可维护性 React 应用的利器。