从零到一实现 react-redux

20 篇文章 2 订阅
8 篇文章 0 订阅

前言

十分钟学会 react-redux 一文中详细讲解了 react-redux 的使用。

从零到一实现 Redux 中,实现了关于 redux 的核心代码。

下面我们按照上一篇的节奏,继续实现一下 react-redux 的核心代码。

本文已收录在 Github: https://github.com/beichensky/Blog 中,欢迎 Star!

核心 API

包含以下核心 API

  • Provider: 上下文组件
  • connect: 带参的高阶函数
  • useSelector: 获取需要的 state 数据
  • useDispatch: 获取 dispatch

一、Provider

全局只有一个 store 对象,需要在多层级组件中传递 store
并且 store 中的 state 发生变化,组件需要相应的做出更新。
所以这里我们使用 Context 进行数据传递

  • 创建 Context
import React, { useContext, useEffect, useReducer } from "react";
import { bindActionCreators } from "./redux";

const StoreContext = React.createContext();
  • 创建 Provider 组件
/**
 * Provider 组件,用来传递 Context 中数据,进行跨层级组件通信
 */
const Provider = ({ store, children }) => (
  // 将 store 作为 value 传递下去
  <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);
export { Provider, connect, useDispatch, useSelector };

二、connect 高阶函数

  • 订阅监听事件,state 发生变化,强制更新组件

  • statePropsdispatchPropsmergeProps 合并到组件的 props

  • APIconnect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])

connect 的用法相对复杂一些,接受四个参数(我们这里暂时不管第四个参数),返回的是一个高阶组件。用来连接当前组件和 Redux store

1. mapStateToProps

mapStateToProps:函数类型,接受两个参数: stateownProps(当前组件的 props,不建议使用,会导致重渲染,损耗性能),必须返回一个纯对象,这个对象会与组件的 props 合并

  • (state[, ownProps]) => ({ count: state.count, todoList: state.todos })

2. mapDispatchToProps

mapDispatchToProps:object | 函数

  • 不传递这个参数时,dispatch 会默认挂载到组件的的 props

  • 传递 object 类型时,会把 object 中的属性值使用 dispatch 包装后,与组件的 props 合并

    • 对象的属性值都必须是 ActionCreator

    • dispatch 不会再挂载到组件的 props

  • 传递函数类型时,接收两个参数:dispatchownProps(当前组件的 props,不建议使用,会导致重渲染,损耗性能),必须返回一个纯对象,这个对象会和组件的 props 合并

3. mergeProps

mergeProps(很少使用) 函数类型。如果指定了这个参数,mapStateToProps()mapDispatchToProps()的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 ActionCreator 绑定在一起。如果你省略这个参数,默认情况下组件的 props 返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果

  • mergeProps(stateProps, dispatchProps, ownProps): props

connect

/**
 * connect 函数,源码中包含四个参数,我们这里只用到这些,所以就暂时只实现了前三个参数
 *
 * @param {*} mapStateToProps 将 state 合并到组件的 props 中的函数
 * @param {*} mapDispatchToProps 将 actionCreator 合并到组件的 props 中的函数
 * @param {*} mergeProps 自定义属性合并到组件的 props
 */
const connect = (
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
) => WrapperComponent => {
  return props => {
    const { getState, dispatch, subscribe } = useContext(StoreContext);
    const [, forceUpdate] = useReducer(x => x + 1, []);
    // 执行 mapStateToProps,获取用户需要的 state 数据
    const stateProps = mapStateToProps(getState());
    // 默认将 dispatch 挂载到 props 上
    let dispatchProps = { dispatch };

    // 判断 mapDispatchToProps 是函数还是对象,函数的话,执行获取返回的对象
    if (typeof mapDispatchToProps === "function") {
      dispatchProps = mapDispatchToProps(dispatch);
    } else if (mapDispatchToProps === "object") {
      // 对象的话,直接将对象中的 actionCreator 使用 dispatch 进行包装
      dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
    }

    mergeProps = mergeProps(stateProps, dispatchProps, props);

    useEffect(() => {
      // 添加事件订阅,state 发生变化时会触发,更新组件
      const unsubscribe = subscribe(() => forceUpdate());
      return () => {
        unsubscribe();
      };
    }, [subscribe]);

    return (
      <WrapperComponent
        {...props}
        {...stateProps}
        {...dispatchProps}
        {...mergeProps}
      />
    );
  };
};

三、useDispatch 获取 dispatch

useDispatch

/**
 * 获取 store 对象
 */
const useStore = () => {
  const store = useContext(StoreContext);
  return store;
};

/**
 * 获取 store 中 dispatch
 */
const useDispatch = () => {
  const store = useStore();
  return store.dispatch;
};

四、useSelector 获取需要的 state 值

  • 订阅事件监听,state 发生变化,强制更新组件

  • 接受一个函数作为参数,函数的返回值作为 useSelector 的返回值传递出去

/**
 * useSelector 从 store 中获取当前组件所需要的 state
 *
 * @param {(state) => props} selector 用户传入的函数,接收 store 当前的 state,返回一个组织好的数据对象
 */
const useSelector = selector => {
  const [, forceUpdate] = useReducer(x => x + 1, []);
  const { subscribe, getState } = useStore();

  useEffect(() => {
    // 添加事件订阅,state 发生变化时会触发,更新组件
    const unsubscribe = subscribe(() => forceUpdate());
    return () => {
      unsubscribe();
    };
  }, [subscribe]);
  return selector(getState());
};

系列文章

写在后面

至此, react-redux 的核心功能基本已经实现,不过有很多细节和参数的兼容都没有进行处理,有兴趣的朋友可以参照源码完善。

源码地址

如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。

如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值