广告位 · 页头(后台-模块管理-新建模块后替换此区域)

Redux的基本概念是什么

Redux 是一个用于管理 JavaScript 应用状态的工具,它把所有需要共享的数据集中到一个地方,并规定只能通过特定方式修改。理解 Redux 的基本概念,可以先把它拆成三个核心对象:Store、Action、Reducer。Store 是保存状态树的容器;Action 表示发生了什么;Reducer 决定状态如何更新。三者通过单向数据流连接起来,从视图派发动作开始,到界面重新渲染结束。

Redux的基本概念是什么

这种设计并不是为了增加复杂度,而是为了解决多组件共享状态时改动不可追踪的问题。当应用规模扩大,组件之间的数据传递会变得越来越混乱,而 Redux 用严格的更新协议让每一次变化都清晰可查。

Store与State:单一数据源

Store 是整个 Redux 应用的中心。它使用 createStore 或 Redux Toolkit 的 configureStore 创建,内部保存一棵 JavaScript 对象树,这棵树就是应用的全部共享状态。状态树一旦放在 Store 中,组件就不能直接修改它。要读取状态,可以调用 store.getState();要触发更新,只能通过 dispatch 派发 Action。这个约束保证了状态来源唯一,排查问题时不需要在十几个组件里寻找谁改了数据。

单一数据源带来的另一个好处是状态可序列化。Store 中的状态通常由普通对象、数组、字符串、数字等组成,便于保存到本地存储、上传到服务端,或者在开发工具中回放。需要特别注意的是,Reducer 不能包含随机数、日期等不确定逻辑,否则同样的动作会产生不同结果,破坏可预测性。

import { createStore } from 'redux';
const initialState = {
  count: 0,
  user: null
};
function rootReducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    default:
      return state;
  }
}
const store = createStore(rootReducer);
console.log(store.getState()); // { count: 0, user: null }

示例中 initialState 是状态树的初始形态,rootReducer 接收旧状态和动作后返回新状态。创建 Store 后,应用其他部分就能通过 store.getState() 访问最新状态。这个例子没有引入视图层,说明 Redux 本身与 React 无关,它可以独立运行在任何 JavaScript 环境中。

Action与Reducer:描述变化

Action 是改变状态的唯一入口。它本质上是一个普通对象,必须包含一个 type 字段,用来描述要执行的操作类型。字段名通常使用大写字符串加下划线,如 ADD_TODO 或 USER_LOGIN。除了 type,Action 还可以携带额外数据,习惯上放到 payload 字段中。Action 本身不直接改变状态,它只是把意图告诉 Store。

为了提高代码可维护性,通常会编写 Action Creator。Action Creator 是一个返回 Action 对象的函数,例如 addTodo(text)。这样可以避免在组件里手写对象字面量,也能统一处理 ID 生成等逻辑。Action Creator 让派发动作的代码更简洁,也更容易测试。

const addTodo = (text) => ({
  type: 'ADD_TODO',
  payload: {
    id: Date.now(),
    text,
    completed: false
  }
});
function todosReducer(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, action.payload];
    case 'TOGGLE_TODO':
      return state.map((todo) =>
        todo.id === action.payload.id
          ? { ...todo, completed: !todo.completed }
          : todo
      );
    default:
      return state;
  }
}

Reducer 是一个纯函数,签名是 (state, action) => newState。它接收当前状态和动作,返回新的状态对象。纯函数意味着同样的输入永远得到同样的输出,并且不能产生副作用,例如修改参数、请求接口、操作 DOM。Reducer 内部通常使用 switch 语句匹配 action.type,对未知类型返回原状态。

不可变更新是 Redux 的核心要求。代码中使用展开运算符创建新数组和新对象,而不是直接修改原状态。这样做可以保留旧状态,让 Redux DevTools 能够比较前后差异,并实现时间旅行调试。如果直接修改 state,引用不会变化,组件可能不会重新渲染,调试工具也会失效。

Dispatch与单向数据流

dispatch 是触发状态更新的方法,调用形式为 store.dispatch(action)。当 Store 收到 Action 后,会同步调用 Reducer 生成新状态,然后把新状态保存下来。状态更新后,Store 会通知所有订阅者。订阅通过 store.subscribe(listener) 注册,返回一个取消订阅函数。组件库如 React Redux 底层就使用这种机制来驱动界面更新。

Redux 的数据流始终是单向的。视图层只能派发 Action,不能直接修改状态;Reducer 只能根据旧状态生成新状态;Store 更新后通知视图。这个闭环没有反向通道,任何状态变化都可以被追踪。单向数据流让代码执行顺序非常明确,也降低了并发更新带来的复杂度。

store.dispatch({ type: 'INCREMENT' });
store.dispatch({ type: 'INCREMENT' });
const unsubscribe = store.subscribe(() => {
  console.log('当前状态:', store.getState());
});
store.dispatch({ type: 'INCREMENT' });
unsubscribe();

这段代码演示了派发和订阅的基本用法。前两次派发不会触发订阅,因为监听器尚未注册;第三次派发后控制台会输出最新状态。取消订阅后再次派发 Action,监听器不会再执行。实际项目中一般不需要手动调用 subscribe,React Redux 会自动处理订阅和更新。

Middleware与异步处理

Middleware 是 Redux 的扩展机制,它位于 dispatch 和 Reducer 之间,可以拦截、延迟、修改或记录 Action。没有中间件时,dispatch 只接受普通 Action 对象;加入中间件后,可以派发函数或 Promise,从而支持异步请求等副作用。常见的中间件包括 redux-thunk、redux-saga 和 redux-observable。

中间件本质上是一个三层函数。第一层接收 Store,第二层接收 next 函数,第三层接收 Action。中间件可以在调用 next(action) 前后添加逻辑,也可以根据条件不调用 next。下面是一个简单的日志中间件,它打印每次派发的动作和更新后的状态。

const logger = (store) => (next) => (action) => {
  console.log('dispatching', action);
  const result = next(action);
  console.log('next state', store.getState());
  return result;
};

异步场景通常使用 redux-thunk。它允许 Action Creator 返回一个函数,该函数接收 dispatch 和 getState,可以在合适的时机派发普通 Action。例如请求开始前派发 FETCH_USER_REQUEST,请求成功后派发 FETCH_USER_SUCCESS。这样异步逻辑虽然写在 Action Creator 中,但最终仍通过 Reducer 更新状态,不破坏单向数据流。

理解这些基本概念后,再接触 Redux Toolkit 会发现它只是对这些概念的封装。createSlice 自动生成 Action Creator 和 Reducer,createAsyncThunk 简化异步流程,但底层仍然是 Store、Action、Reducer 和 Middleware 的协作。把握住这套协议,遇到新的 Redux 工具或中间件时,就更容易判断它处在数据流的哪个环节,以及它解决了什么问题。

广告位 · 文章底部(后台-模块管理替换此区域)