ByAI: Redux的typescript简化实现

Redux 是一个用于 JavaScript 应用的可预测状态容器,其核心原理基于 ​单向数据流 ​ 和 ​纯函数


一、Redux 核心原理

  1. 三大原则

    • 单一数据源:整个应用状态存储在一个全局 Store 中。
    • 状态只读 :只能通过 dispatch(action) 修改状态。
    • 纯函数更新 :通过 reducer 纯函数计算新状态。
  2. 关键角色

    • Store :保存状态,提供 dispatch/subscribe/getState 方法。
    • Action :描述变化的普通对象({ type: 'ACTION_TYPE', payload })。
    • Reducer :纯函数 (state, action) => newState

二、TypeScript 实现

1. 定义类型

typescript 复制代码
// Action 类型:基础 Action 必须包含 type 字段
type Action = {
  type: string;
  [key: string]: any; // 允许其他任意字段(如 payload)
};

// Reducer 类型:接收当前 state 和 action,返回新 state
type Reducer<S = any, A extends Action = Action> = (state: S, action: A) => S;

// Store 类型:包含核心方法
interface Store<S = any, A extends Action = Action> {
  getState(): S;
  dispatch(action: A): void;
  subscribe(listener: () => void): () => void; // 返回取消订阅函数
}

2. 创建 Store

typescript 复制代码
function createStore<S, A extends Action>(
  reducer: Reducer<S, A>,
  initialState: S
): Store<S, A> {
  let state = initialState;
  const listeners: Array<() => void> = [];

  // 获取当前状态
  const getState = (): S => state;

  // 分发 Action
  const dispatch = (action: A): void => {
    state = reducer(state, action); // 调用 reducer 计算新状态
    listeners.forEach(listener => listener()); // 通知所有订阅者
  };

  // 订阅状态变化
  const subscribe = (listener: () => void): () => void => {
    listeners.push(listener);
    // 返回取消订阅函数
    return () => {
      const index = listeners.indexOf(listener);
      if (index !== -1) listeners.splice(index, 1);
    };
  };

  // 初始化时触发一次 dispatch(可选,用于兼容某些中间件)
  dispatch({ type: '@@INIT' } as A);

  return { getState, dispatch, subscribe };
}

3. 组合多个 Reducer(combineReducers)

typescript 复制代码
function combineReducers<Reducers extends Record<string, Reducer>>(
  reducers: Reducers
): Reducer {
  return (state: any = {}, action: Action): any => {
    const newState: any = {};
    Object.keys(reducers).forEach(key => {
      newState[key] = reducers[key](state[key], action); // 分别调用子 reducer
    });
    return newState;
  };
}

三、完整使用示例

typescript 复制代码
// 1. 定义 State 和 Action 类型
interface CounterState {
  count: number;
}

type CounterAction = 
  | { type: 'INCREMENT' }
  | { type: 'DECREMENT' }
  | { type: 'SET_COUNT'; payload: number };

// 2. 实现 Reducer
const counterReducer: Reducer<CounterState, CounterAction> = (
  state = { count: 0 },
  action
) => {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    case 'SET_COUNT':
      return { count: action.payload };
    default:
      return state;
  }
};

// 3. 创建 Store
const store = createStore(counterReducer, { count: 0 });

// 4. 订阅状态变化
const unsubscribe = store.subscribe(() => {
  console.log('State changed:', store.getState());
});

// 5. 分发 Action
store.dispatch({ type: 'INCREMENT' }); // State: { count: 1 }
store.dispatch({ type: 'SET_COUNT', payload: 10 }); // State: { count: 10 }

// 6. 取消订阅
unsubscribe();

四、关键设计解析

  1. 不可变状态
    Reducer 必须返回新对象(如 { ...state, count: state.count + 1 }),而不是直接修改原 state。
  2. 时间旅行调试
    由于状态是纯函数计算的结果,可以轻松记录 action 和 state 的历史,实现时间旅行功能(如 Redux DevTools)。
  3. 中间件扩展
    实际 Redux 通过 applyMiddleware 增强 dispatch 功能(如异步处理),这里未实现,但原理是通过包装 dispatch 方法。
相关推荐
java干货2 分钟前
深度解析:Spring Boot 配置加载顺序、优先级与 bootstrap 上下文
前端·spring boot·bootstrap
Uyker22 分钟前
微信小程序动态效果实战指南:从悬浮云朵到丝滑列表加载
前端·微信小程序·小程序
小小小小宇1 小时前
前端按需引入总结
前端
小小小小宇1 小时前
React 的 DOM diff笔记
前端
小小小小宇1 小时前
react和vue DOM diff 简单对比
前端
我在北京coding1 小时前
6套bootstrap后台管理界面源码
前端·bootstrap·html
Carlos_sam1 小时前
Opnelayers:封装Popup
前端·javascript
前端小白从0开始2 小时前
Vue3项目实现WPS文件预览和内容回填功能
前端·javascript·vue.js·html5·wps·文档回填·文档在线预览
難釋懷3 小时前
Vue解决开发环境 Ajax 跨域问题
前端·vue.js·ajax