ByAI:Rect-redux实现及connect函数

一、React-Redux 核心原理

  1. Provider

    通过 React Context 提供 Redux store 给子组件,避免逐层传递 props。

  2. connect

    高阶组件(HOC)负责:

    • 订阅 store 变化
    • 将 state 和 dispatch 映射为组件的 props
    • 性能优化(避免不必要的渲染)

二、TypeScript 实现

1. 类型定义

typescript 复制代码
import React, { ComponentType, useContext, useEffect, useMemo } from 'react';
import { Store, AnyAction, Dispatch } from 'redux';

// 定义 Connect 的 Props 类型
type OwnProps = Record<string, any>; // 组件自身的 props 类型

// connect 的配置参数类型
interface ConnectOptions<State = any, OP = OwnProps> {
  mapStateToProps?: (state: State, ownProps: OP) => Partial<OP>;
  mapDispatchToProps?: (dispatch: Dispatch<AnyAction>, ownProps: OP) => Partial<OP>;
}

// 连接后的组件 Props 类型
type ConnectedProps<State, OP> = OP & {
  [K in keyof ReturnType<ConnectOptions<State, OP>>]: 
    ReturnType<ConnectOptions<State, OP>>[K];
};

2. Provider 实现

typescript 复制代码
const StoreContext = React.createContext<Store<any, AnyAction> | null>(null);

export const Provider: React.FC<{ store: Store<any, AnyAction> }> = ({ store, children }) => (
  <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);

3. connect 实现

typescript 复制代码
export function connect<State = any, OP = OwnProps>(
  options: ConnectOptions<State, OP>
) {
  return function <C extends ComponentType<ConnectedProps<State, OP>>>(
    WrappedComponent: C
  ) {
    return function ConnectedComponent(props: OP) {
      const store = useContext(StoreContext);
      if (!store) throw new Error('Missing Provider');

      // 获取当前 state 和 dispatch
      const { getState, subscribe, dispatch } = store;

      // 处理 mapStateToProps
      const mapStateToPropsResult = options.mapStateToProps
        ? options.mapStateToProps(getState(), props)
        : {};

      // 处理 mapDispatchToProps
      const mapDispatchToPropsResult = options.mapDispatchToProps
        ? options.mapDispatchToProps(dispatch, props)
        : { dispatch };

      // 合并 props
      const mergedProps = useMemo(() => ({
        ...props,
        ...mapStateToPropsResult,
        ...mapDispatchToPropsResult
      }), [props, mapStateToPropsResult, mapDispatchToPropsResult]);

      // 订阅 store 变化
      useEffect(() => {
        const unsubscribe = subscribe(() => {
          // 当 store 变化时强制更新组件
          forceUpdate();
        });
        return unsubscribe;
      }, [store]);

      // 强制更新机制
      const [, forceUpdate] = React.useReducer(x => x + 1, 0);

      // 性能优化:仅在相关 state 变化时更新
      const stateSelector = options.mapStateToProps 
        ? (state: State) => options.mapStateToProps(state, props)
        : () => ({});

      const prevSelectedStateRef = React.useRef<any>();
      const shouldUpdate = useMemo(() => {
        const currentSelectedState = stateSelector(getState());
        if (prevSelectedStateRef.current === undefined) {
          prevSelectedStateRef.current = currentSelectedState;
          return true;
        }
        const keys = Object.keys(currentSelectedState);
        return keys.some(key => 
          currentSelectedState[key] !== prevSelectedStateRef.current[key]
        );
      }, [getState, props]);

      useEffect(() => {
        if (shouldUpdate) {
          forceUpdate();
          prevSelectedStateRef.current = stateSelector(getState());
        }
      }, [shouldUpdate]);

      return <WrappedComponent {...mergedProps as ConnectedProps<State, OP>} />;
    };
  };
}

4. 使用示例

typescript 复制代码
// 定义 Redux store 类型
interface AppState {
  count: number;
}

// 定义组件 Props
interface CounterProps {
  count: number;
  increment: () => void;
  decrement: () => void;
}

// 创建组件
const Counter: React.FC<CounterProps> = ({ count, increment, decrement }) => (
  <div>
    <p>Count: {count}</p>
    <button onClick={increment}>+</button>
    <button onClick={decrement}>-</button>
  </div>
);

// 连接 Redux
const ConnectedCounter = connect<AppState, CounterProps>({
  mapStateToProps: (state) => ({
    count: state.count
  }),
  mapDispatchToProps: (dispatch) => ({
    increment: () => dispatch({ type: 'INCREMENT' }),
    decrement: () => dispatch({ type: 'DECREMENT' })
  })
})(Counter);

// 在应用中使用
const App = () => (
  <Provider store={store}>
    <ConnectedCounter />
  </Provider>
);

三、核心机制详解

1. mapStateToProps

  • 作用​:将 Redux store 的 state 映射为组件的 props

  • 实现要点​:

    • 接收当前 state 和组件自身 props
    • 返回一个对象,对象的 key 会成为组件的 props
    • 通过浅比较优化更新(避免不必要的渲染)

2. mapDispatchToProps

  • 作用​:将 dispatch 方法映射为组件的 props

  • 两种形式​:

    1. 对象形式:{ increment: () => ({ type: 'INCREMENT' }) }
    2. 函数形式:(dispatch) => ({ increment: () => dispatch(...) })
  • 实现要点​:

    • 自动绑定 dispatch 方法
    • 支持直接传递 action creators 对象

3. 性能优化

  • 订阅机制 :通过 subscribe 监听 store 变化
  • 浅比较:仅当映射后的 state 变化时才触发更新
  • 依赖收集 :通过 useMemo 缓存计算结果

四、与官方实现的差异

  1. 简化了批量更新机制(官方使用 React 的批量更新 API)
  2. 未实现合并 props 的高级功能(如 mergeProps
  3. 类型系统做了简化(官方有更复杂的泛型处理)

这个实现保留了 React-Redux 的核心功能,同时通过 TypeScript 提供了完整的类型安全支持。实际开发中建议直接使用官方库,但理解其原理对深入掌握 React 和 Redux 非常重要。

相关推荐
程序员黑豆8 小时前
Java 注释详解:单行、多行与文档注释的完整指南
java·前端·ai编程
剪刀石头布啊8 小时前
antd中可编辑表格中巧用useWatch实现联动高性能效果,以及定制延伸学习
前端
油丶酸萝卜别吃9 小时前
前端转全栈学习路线
前端·学习
浮生望9 小时前
前端路由进阶:History API原理与手写HistoryRouter
前端
陆枫Larry10 小时前
JavaScript 中的竞态是什么,为啥会有竟态?
前端
上海安当技术12 小时前
半天接入:USBKey RESTful API + C 动态库,Web 和 C/S 两套集成路径实战
前端·后端·restful·集成·usbkey
DevUI团队12 小时前
从“即兴创作”到“规格先行”,华为云码道(CodeArts)代码智能体持续深耕企业级规范驱动开发能力
前端·人工智能·后端
weixin_4316004413 小时前
NestJS 入门(3):Guard 如何挡住未登录请求?
前端·后端·学习·nest.js
kyriewen13 小时前
我用Claude Code两天干完了团队两周的排期——周报发出去那一刻我就后悔了
前端·javascript·ai编程
IT_陈寒14 小时前
JavaScript类型转换把我坑惨了,这破玩意真该早点搞明白
前端·人工智能·后端