Skip to content
imshengli blog
Go back

Redux 状态管理:原则、数据流与中间件机制

Redux 状态管理详解:三大原则、Store/Action/Reducer、中间件机制,以及与 React 的配合使用。

· 6 min

概述

Redux 适用场景:多交互、多数据源。

从组件角度看:某个组件的状态需要共享;某个状态需要在任何地方都可以拿到;一个组件需要改变全局状态或另一个组件的状态。

设计思想:

  1. Web 应用是一个状态机,视图与状态是一一对应的。
  2. 所有的状态,保存在一个对象里面。

三大原则

单一数据源 — 整个应用的 state 存储在唯一的 Store 中。

State 是只读的 — 唯一改变 state 的方法是触发 action,不能直接修改。

使用纯函数执行修改 — Reducer 是纯函数,接收旧 state 和 action,返回新 state。


核心概念

Store

保存数据的地方,整个应用只有一个。

import { createStore } from 'redux';
const store = createStore(reducer);

store.getState();        // 获取当前 state
store.dispatch(action);  // 分发 action
store.subscribe(fn);     // 注册监听器

Action

普通对象,描述”发生了什么”,必须包含 type 字段。

// Action Creator — 生成 Action 的函数
function addTodo(text) {
  return { type: 'ADD_TODO', payload: { text, id: Date.now() } };
}
store.dispatch(addTodo('学习 Redux'));

Reducer

纯函数,接收当前 state 和 action,计算并返回新的 state。

function counterReducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
}

数据流

用户操作 → dispatch(action) → Reducer(state, action) → 新 state → 视图更新

完整示例:计数器

const { createStore } = require('redux');

// Action Types & Creators
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const RESET = 'RESET';

function increment() { return { type: INCREMENT }; }
function decrement() { return { type: DECREMENT }; }
function reset() { return { type: RESET }; }

// Reducer
function counterReducer(state = 0, action) {
  switch (action.type) {
    case INCREMENT: return state + 1;
    case DECREMENT: return state - 1;
    case RESET: return 0;
    default: return state;
  }
}

// 创建 Store 并使用
const store = createStore(counterReducer);
store.subscribe(() => console.log('计数:', store.getState()));

store.dispatch(increment()); // 计数: 1
store.dispatch(increment()); // 计数: 2
store.dispatch(decrement()); // 计数: 1
store.dispatch(reset());     // 计数: 0

完整示例:TodoList

const { createStore } = require('redux');

// Action Creators
function addTodo(text) {
  return { type: 'ADD_TODO', payload: { text, id: Date.now() } };
}
function toggleTodo(id) {
  return { type: 'TOGGLE_TODO', payload: { id } };
}

// Reducer
function todoReducer(state = { todos: [] }, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return { ...state, todos: [...state.todos, {
        id: action.payload.id, text: action.payload.text, completed: false
      }]};
    case 'TOGGLE_TODO':
      return { ...state, todos: state.todos.map(todo =>
        todo.id === action.payload.id
          ? { ...todo, completed: !todo.completed } : todo
      )};
    default:
      return state;
  }
}

const store = createStore(todoReducer);
store.subscribe(() => {
  const { todos } = store.getState();
  console.log(todos.map(t => `${t.completed ? '' : ''} ${t.text}`));
});

store.dispatch(addTodo('学习 Redux'));
store.dispatch(addTodo('写代码'));

Reducer 拆分与合并

import { combineReducers, createStore } from 'redux';

function userReducer(state = null, action) {
  switch (action.type) {
    case 'LOGIN': return action.payload;
    case 'LOGOUT': return null;
    default: return state;
  }
}

function todosReducer(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO': return [...state, action.payload];
    default: return state;
  }
}

const rootReducer = combineReducers({
  user: userReducer,
  todos: todosReducer
});
// state 结构: { user: null, todos: [] }

中间件 (Middleware)

中间件在 action 到达 reducer 之前进行拦截处理,常用于日志、异步操作等。

无中间件:   dispatch(action) → Reducer → newState
有中间件:   dispatch(action) → Middleware → ... → Reducer → newState

日志中间件

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

异步中间件 (redux-thunk 原理)

// redux-thunk 核心实现
const thunk = store => next => action => {
  if (typeof action === 'function') {
    return action(store.dispatch, store.getState);
  }
  return next(action);
};

// 使用:异步 Action Creator
function fetchUser(userId) {
  return async (dispatch) => {
    dispatch({ type: 'FETCH_USER_START' });
    try {
      const res = await fetch(`/api/users/${userId}`);
      const user = await res.json();
      dispatch({ type: 'FETCH_USER_SUCCESS', payload: user });
    } catch (error) {
      dispatch({ type: 'FETCH_USER_ERROR', payload: error.message });
    }
  };
}

应用中间件

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

const store = createStore(rootReducer, applyMiddleware(logger, thunk));

与 React 配合

通过 react-redux 连接 Redux 和 React。

import { Provider, connect } from 'react-redux';

// 1. Provider 在顶层注入 Store
function App() {
  return (
    <Provider store={store}>
      <TodoApp />
    </Provider>
  );
}

// 2. connect 连接组件
function TodoList({ todos, onToggle }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id} onClick={() => onToggle(todo.id)}>
          {todo.text}
        </li>
      ))}
    </ul>
  );
}

const mapStateToProps = state => ({ todos: state.todos });
const mapDispatchToProps = dispatch => ({
  onToggle: id => dispatch(toggleTodo(id))
});

export default connect(mapStateToProps, mapDispatchToProps)(TodoList);

完整数据流

用户点击 → props 方法 → dispatch(action) → 中间件
→ Reducer → Store 更新 → mapStateToProps → 组件重新渲染

总结

Redux 核心思路:用 Store 存所有状态,用 Action 描述事件,用 Reducer 纯函数计算新状态。

适合 Redux:大量共享状态、复杂更新逻辑、需要状态追踪。不需要 Redux:简单 UI 状态、数据只在父子间传递、应用规模小。


参考文档


Share this post on:

Previous Post
Vue.js 入门:数据绑定、指令与组件基础
Next Post
ES6 Async/Await:异步编程的终极方案