手写react-redux

209 阅读3分钟

前言

在上文手写redux中,我们实现了数据响应式流向,但是在react中使用还是过于繁琐了,比如数据监听,卸载,多dispatch调用。社区中有人为了解决这个问题就写了基于redux数据源的新插件react-redux,这次就来实现该代码,更加深入了解react-redux的诞生原因,及实现原理。

react-redux

react-redux中的两大核心就是connect,Provider。

  1. 使用createContext来进行跨层级数据传递(store)
  2. 使用connect返回新组件(高阶组件思想)

Provider

创建Context进行跨组件数据传递

// 创建上下文(Context),传递store
const Context = React.createContext();

// 通过Provider传递value(接收到的store)
export function Provider({ store, children }) {
  return <Context.Provider value={store}>{children}</Context.Provider>
}

// 使用页
import { Provider } from "./react-redux";
import store from './store';
import Index from "./pages/index";
// 传递store数据
function App() {
  return (
    <Provider store={store}>
      <div className="App">
        <Index/>
      </div>
    </Provider>
  );
}

connect

实现操作流程简化的核心函数,设计思想是通过创建一个新的函数组件,为它赋予增强后的props属性,及各类操作。 用户使用时,就可专注于action操作,而不需每次操作时都使用dispatch。还实现了自动数据监听,自动卸载等。

// connect,是一个高阶组件(参数是组件,返回值是组件)
export function connect(mapStateToProps, mapDispatchToProps) {

  // 第一个返回函数接收一个组件
  return function(WrappedComponent) {
    // 第二个返回函数接收props, 这里的返回值其实就是 WrappedIndex(是一个函数式组件,可接收props进行其他逻辑操作)
    return function(props) {
      // console.log("connect props", props);
      
      const store = useContext(Context); // 拿到接收的数据(Provider传递的)
      const { getState, dispatch, subscribe } = store;
      const stateProps = mapStateToProps(getState()); // 返回store中的state(初始化)

      // 合并dispatch选项,让用户可以直接调用dispatch传递action,进行操作。
      let dispatchProps = { };
      if (typeof mapDispatchToProps === "object") { // 是对象则合并,并保存传递的函数
        dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
      }
      if (typeof mapDispatchToProps === "function") { // 是函数则直接调用并传入dispatch给用户使用
        dispatchProps = mapDispatchToProps(dispatch);
      }

      // 更新
      const forceUpdate = useForceUpdate();

      // 初始化监听和卸载时取消监听
      useLayoutEffect(() => {
        const unsubscribe = subscribe(() => {
          forceUpdate();
        })
        return () => {
          unsubscribe && unsubscribe()
        }
      }, [store])

      // 返回第一个函数接收的组件,并给该组件 添加props(接收到的props,mapstateProps,dispatchProps)。
      // 这样就将组件中需要的数据,操作等参数通过props的方式传递下去了。并且帮助该组件自动完成了数据监听(更新组件),组件销毁时(卸载监听)。
      return <WrappedComponent {...props} {...stateProps} {...dispatchProps} dispatch={dispatch}/>
    }
  }
}

useDispatch、useForceUpdate、bindActionCreators、bindActionCreator

一些辅助函数

// 使用hook返回dispatch
export function useDispatch() {
  const store = useContext(Context);
  return store.dispatch;
}

// 使用函数hooks创建一个自增的更新函数。当state变化时 就能触发更新
function useForceUpdate() {
  const [, setState] = useState(0);
  const update = useCallback(() => {
    setState(prev => prev + 1)
  }, [])
  return update;
}

export function bindActionCreators(creators, dispatch) {
  const obj = {};
  for (const key in creators) {
    obj[key] = bindActionCreator(creators[key], dispatch);
  }
  return obj;
}

function bindActionCreator(creator, dispatch) {
  // 这里的args,是触发这个函数的宿主元素(input => onChange, div => onClick);
  // 该函数绑定在谁身上触发,或者手动调用触发,接收的宿主或传递的参数
  return (...args) => dispatch(creator(...args))
}

结语

至此我们便完成了react-redux的大致实现,写完这个代码后我对它理解更深了,核心思想就是高阶组件+Context。短短几十行代码,但是实现的东西很巧妙。github代码地址