乐趣区

手写一个ReactRedux玩转React的Context-API

上一篇文章咱们手写了一个 Redux,然而单纯的 Redux 只是一个状态机,是没有 UI 出现的,所以个别咱们应用的时候都会配合一个 UI 库,比方在 React 中应用 Redux 就会用到 React-Redux 这个库。这个库的作用是将 Redux 的状态机和 React 的 UI 出现绑定在一起,当你 dispatch action 扭转 state 的时候,会自动更新页面。本文还是从它的根本应用动手来本人写一个React-Redux,而后替换官网的 NPM 库,并放弃性能统一。

本文全副代码曾经上传 GitHub,大家能够拿下来玩玩:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-redux

根本用法

上面这个简略的例子是一个计数器,跑起来成果如下:

要实现这个性能,首先咱们要在我的项目外面增加 react-redux 库,而后用它提供的 Provider 包裹整个ReactApp 的根组件:

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux'
import store from './store'
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

下面代码能够看到咱们还给 Provider 提供了一个参数 store,这个参数就是 Redux 的createStore 生成的 store,咱们须要调一下这个办法,而后将返回的store 传进去:

import {createStore} from 'redux';
import reducer from './reducer';

let store = createStore(reducer);

export default store;

下面代码中 createStore 的参数是一个reducer,所以咱们还要写个reducer:

const initState = {count: 0};

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

export default reducer;

这里的 reduce 会有一个初始 state,外面的count0,同时他还能解决三个 action,这三个action 对应的是 UI 上的三个按钮,能够对 state 外面的计数进行加减和重置。到这里其实咱们 React-Redux 的接入和 Redux 数据的组织其实曾经实现了,前面如果要用 Redux 外面的数据的话,只须要用 connectAPI 将对应的state 和办法连贯到组件外面就行了,比方咱们的计数器组件须要 count 这个状态和加一,减一,重置这三个 action,咱们用connect 将它连贯进去就是这样:

import React from 'react';
import {connect} from 'react-redux';
import {increment, decrement, reset} from './actions';

function Counter(props) {
  const { 
    count,
    incrementHandler,
    decrementHandler,
    resetHandler
   } = props;

  return (
    <>
      <h3>Count: {count}</h3>
      <button onClick={incrementHandler}> 计数 +1</button>
      <button onClick={decrementHandler}> 计数 -1</button>
      <button onClick={resetHandler}> 重置 </button>
    </>
  );
}

const mapStateToProps = (state) => {
  return {count: state.count}
}

const mapDispatchToProps = (dispatch) => {
  return {incrementHandler: () => dispatch(increment()),
    decrementHandler: () => dispatch(decrement()),
    resetHandler: () => dispatch(reset()),
  }
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter)

下面代码能够看到 connect 是一个高阶函数,他的第一阶会接管 mapStateToPropsmapDispatchToProps两个参数,这两个参数都是函数。mapStateToProps能够自定义须要将哪些 state 连贯到以后组件,这些自定义的 state 能够在组件外面通过 props 拿到。mapDispatchToProps办法会传入 dispatch 函数,咱们能够自定义一些办法,这些办法能够调用 dispatchdispatch action,从而触发 state 的更新,这些自定义的办法也能够通过组件的 props 拿到,connect的第二阶接管的参数是一个组件,咱们能够猜想这个函数的作用就是将后面自定义的 state 和办法注入到这个组件外面,同时要返回一个新的组件给内部调用,所以 connect 其实也是一个高阶组件。

到这里咱们汇总来看下咱们都用到了哪些 API,这些 API 就是咱们前面要手写的指标:

Provider: 用来包裹根组件的组件,作用是注入 Reduxstore

createStore: Redux用来创立 store 的外围办法,[咱们另一篇文章曾经手写过了]()。

connect:用来将 statedispatch注入给须要的组件,返回一个新组件,他其实是个高阶组件。

所以 React-Redux 外围其实就两个 API,而且两个都是组件,作用还很相似,都是往组件外面注入参数,Provider是往根组件注入 storeconnect 是往须要的组件注入 statedispatch

在手写之前咱们先来思考下,为什么 React-Redux 要设计这两个 API,如果没有这两个 API,只用 Redux 能够吗?当然是能够的!其实咱们用 Redux 的目标不就是心愿用它将整个利用的状态都保留下来,每次操作只用 dispatch action 去更新状态,而后 UI 就自动更新了吗?那我从根组件开始,每一级都把 store 传下去不就行了吗?每个子组件须要读取状态的时候,间接用 store.getState() 就行了,更新状态的时候就 store.dispatch,这样其实也能达到目标。然而,如果这样写,子组件如果嵌套层数很多,每一级都须要手动传入store,比拟俊俏,开发也比拟繁琐,而且如果某个新同学忘了传store,那前面就是一连串的谬误了。所以最好有个货色可能将store 全局的注入组件树,而不须要一层层作为 props 传递,这个货色就是 Provider!而且如果每个组件都独立依赖Redux 会毁坏 React 的数据流向,这个咱们前面会讲到。

React 的 Context API

React 其实提供了一个全局注入变量的 API,这就是 context api。如果我当初有一个需要是要给咱们所有组件传一个文字色彩的配置,咱们的色彩配置在最顶级的组件上,当这个色彩扭转的时候,上面所有组件都要主动利用这个色彩。那咱们能够应用 context api 注入这个配置:

先应用 React.createContext 创立一个 context

// 咱们应用一个独自的文件来调用 createContext
// 因为这个返回值会被 Provider 和 Consumer 在不同的中央援用
import React from 'react';

const TestContext = React.createContext();

export default TestContext;

应用 Context.Provider 包裹根组件

创立好了 context,如果咱们要传递变量给某些组件,咱们须要在他们的根组件上加上 TestContext.Provider,而后将变量作为value 参数传给TestContext.Provider:

import TestContext from './TestContext';

const setting = {color: '#d89151'}

ReactDOM.render(<TestContext.Provider value={setting}>
      <App />
  </TestContext.Provider>,
  document.getElementById('root')
);

应用 Context.Consumer 接管参数

下面咱们应用 Context.Provider 将参数传递进去了,这样被 Context.Provider 包裹的所有子组件都能够拿到这个变量,只是拿这个变量的时候须要应用 Context.Consumer 包裹,比方咱们后面的 Counter 组件就能够拿到这个色彩了,只须要将它返回的 JSXContext.Consumer包裹一下就行:

// 留神要引入同一个 Context
import TestContext from './TestContext';

// ... 两头省略 n 行代码 ...
// 返回的 JSX 用 Context.Consumer 包裹起来
// 留神 Context.Consumer 外面是一个办法,这个办法就能够拜访到 context 参数
// 这里的 context 也就是后面 Provider 传进来的 setting,咱们能够拿到下面的 color 变量
return (
    <TestContext.Consumer>
      {context => 
        <>
          <h3 style={{color:context.color}}>Count: {count}</h3>
          <button onClick={incrementHandler}> 计数 +1</button>&nbsp;&nbsp;
          <button onClick={decrementHandler}> 计数 -1</button>&nbsp;&nbsp;
          <button onClick={resetHandler}> 重置 </button>
        </>
      }
    </TestContext.Consumer>
  );

下面代码咱们通过 context 传递了一个全局配置,能够看到咱们文字色彩曾经变了:

应用 useContext 接管参数

除了下面的 Context.Consumer 能够用来接管 context 参数,新版 React 还有 useContext 这个 hook 能够接管 context 参数,应用起来更简略,比方下面代码能够这样写:

const context = useContext(TestContext);

return (
    <>
      <h3 style={{color:context.color}}>Count: {count}</h3>
      <button onClick={incrementHandler}> 计数 +1</button>&nbsp;&nbsp;
      <button onClick={decrementHandler}> 计数 -1</button>&nbsp;&nbsp;
      <button onClick={resetHandler}> 重置 </button>
    </>
);

所以咱们齐全能够用 context api 来传递 redux store,当初咱们也能够猜想React-ReduxProvider其实就是包装了 Context.Provider,而传递的参数就是redux store,而React-ReduxconnectHOC 其实就是包装的 Context.Consumer 或者 useContext。咱们能够依照这个思路来本人实现下React-Redux 了。

手写Provider

下面说了 Provider 用了 context api,所以咱们要先建一个context 文件,导出须要用的context

// Context.js
import React from 'react';

const ReactReduxContext = React.createContext();

export default ReactReduxContext;

这个文件很简略,新建一个 context 再导出就行了,对应的源码看这里。

而后将这个 context 利用到咱们的 Provider 组件外面:

import React from 'react';
import ReactReduxContext from './Context';

function Provider(props) {const {store, children} = props;

  // 这是要传递的 context
  const contextValue = {store};

  // 返回 ReactReduxContext 包裹的组件,传入 contextValue
  // 外面的内容就间接是 children,咱们不动他
  return (<ReactReduxContext.Provider value={contextValue}>
      {children}
    </ReactReduxContext.Provider>
  )
}

Provider的组件代码也不难,间接将传进来的 store 放到 context 上,而后间接渲染 children 就行,对应的源码看这里。

手写connect

基本功能

其实 connect 才是 React-Redux 中最难的局部,外面性能简单,思考的因素很多,想要把它搞明确咱们须要一层一层的来看,首先咱们实现一个只具备基本功能的connect

import React, {useContext} from 'react';
import ReactReduxContext from './Context';

// 第一层函数接管 mapStateToProps 和 mapDispatchToProps
function connect(mapStateToProps, mapDispatchToProps) {
  // 第二层函数是个高阶组件,外面获取 context
  // 而后执行 mapStateToProps 和 mapDispatchToProps
  // 再将这个后果组合用户的参数作为最终参数渲染 WrappedComponent
  // WrappedComponent 就是咱们应用 connext 包裹的本人的组件
  return function connectHOC(WrappedComponent) {function ConnectFunction(props) {
      // 复制一份 props 到 wrapperProps
      const {...wrapperProps} = props;

      // 获取 context 的值
      const context = useContext(ReactReduxContext);

      const {store} = context;  // 解构出 store
      const state = store.getState();   // 拿到 state

      // 执行 mapStateToProps 和 mapDispatchToProps
      const stateProps = mapStateToProps(state);
      const dispatchProps = mapDispatchToProps(store.dispatch);

      // 组装最终的 props
      const actualChildProps = Object.assign({}, stateProps, dispatchProps, wrapperProps);

      // 渲染 WrappedComponent
      return <WrappedComponent {...actualChildProps}></WrappedComponent>
    }

    return ConnectFunction;
  }
}

export default connect;

触发更新

用下面的 Providerconnect替换官网的 react-redux 其实曾经能够渲染出页面了,然而点击按钮还不会有反馈,因为咱们尽管通过 dispatch 扭转了 store 中的 state,然而这种扭转并没有触发咱们组件的更新。之前 Redux 那篇文章讲过,能够用store.subscribe 来监听 state 的变动并执行回调,咱们这里须要注册的回调是查看咱们最终给 WrappedComponentprops有没有变动,如果有变动就从新渲染ConnectFunction,所以这里咱们须要解决两个问题:

  1. 当咱们 state 变动的时候查看最终给到 ConnectFunction 的参数有没有变动
  2. 如果这个参数有变动,咱们须要从新渲染ConnectFunction

查看参数变动

要查看参数的变动,咱们须要晓得上次渲染的参数和本地渲染的参数,而后拿过去比一下就晓得了。为了晓得上次渲染的参数,咱们能够间接在 ConnectFunction 外面应用 useRef 将上次渲染的参数记录下来:

// 记录上次渲染参数
const lastChildProps = useRef();
useLayoutEffect(() => {lastChildProps.current = actualChildProps;}, []);

留神 lastChildProps.current 是在第一次渲染完结后赋值,而且须要应用 useLayoutEffect 来保障渲染后立刻同步执行。

因为咱们检测参数变动是须要从新计算actualChildProps,计算的逻辑其实都是一样的,咱们将这块计算逻辑抽出来,成为一个独自的办法childPropsSelector:

function childPropsSelector(store, wrapperProps) {const state = store.getState();   // 拿到 state

  // 执行 mapStateToProps 和 mapDispatchToProps
  const stateProps = mapStateToProps(state);
  const dispatchProps = mapDispatchToProps(store.dispatch);

  return Object.assign({}, stateProps, dispatchProps, wrapperProps);
}

而后就是注册 store 的回调,在外面来检测参数是否变了,如果变了就强制更新以后组件,比照两个对象是否相等,React-Redux外面是采纳的 shallowEqual,也就是浅比拟,也就是只比照一层,如果你mapStateToProps 返回了好几层构造,比方这样:

{
  stateA: {value: 1}
}

你去改了 stateA.value 是不会触发从新渲染的,React-Redux这样设计我想是出于性能思考,如果是深比拟,比方递归去比拟,比拟节约性能,而且如果有循环援用还可能造成死循环。采纳浅比拟就须要用户遵循这种范式,不要传入多层构造,这点在官网文档中也有阐明。咱们这里间接抄一个它的浅比拟:

// shallowEqual.js 
function is(x, y) {if (x === y) {return x !== 0 || y !== 0 || 1 / x === 1 / y} else {return x !== x && y !== y}
}

export default function shallowEqual(objA, objB) {if (is(objA, objB)) return true

  if (
    typeof objA !== 'object' ||
    objA === null ||
    typeof objB !== 'object' ||
    objB === null
  ) {return false}

  const keysA = Object.keys(objA)
  const keysB = Object.keys(objB)

  if (keysA.length !== keysB.length) return false

  for (let i = 0; i < keysA.length; i++) {
    if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) ||
      !is(objA[keysA[i]], objB[keysA[i]])
    ) {return false}
  }

  return true
}

在回调外面检测参数变动:

// 注册回调
store.subscribe(() => {const newChildProps = childPropsSelector(store, wrapperProps);
  // 如果参数变了,记录新的值到 lastChildProps 上
  // 并且强制更新以后组件
  if(!shallowEqual(newChildProps, lastChildProps.current)) {
    lastChildProps.current = newChildProps;

    // 须要一个 API 来强制更新以后组件
  }
});

强制更新

要强制更新以后组件的办法不止一个,如果你是用的 Class 组件,你能够间接 this.setState({}),老版的React-Redux 就是这么干的。然而新版 React-Redux 用 hook 重写了,那咱们能够用 React 提供的 useReducer 或者 useStatehook,React-Redux 源码用了useReducer,为了跟他保持一致,我也应用useReducer:

function storeStateUpdatesReducer(count) {return count + 1;}

// ConnectFunction 外面
function ConnectFunction(props) {
  // ... 后面省略 n 行代码 ... 
  
  // 应用 useReducer 触发强制更新
  const [
    ,
    forceComponentUpdateDispatch
  ] = useReducer(storeStateUpdatesReducer, 0);
  // 注册回调
  store.subscribe(() => {const newChildProps = childPropsSelector(store, wrapperProps);
    if(!shallowEqual(newChildProps, lastChildProps.current)) {
      lastChildProps.current = newChildProps;
      forceComponentUpdateDispatch();}
  });
  
  // ... 前面省略 n 行代码 ...
}

connect这块代码次要对应的是源码中 connectAdvanced 这个类,基本原理和构造跟咱们这个都是一样的,只是他写的更灵便,反对用户传入自定义的 childPropsSelector 和合并 stateProps, dispatchProps, wrapperProps 的办法。有趣味的敌人能够去看看他的源码:https://github.com/reduxjs/react-redux/blob/master/src/components/connectAdvanced.js

到这里其实曾经能够用咱们本人的 React-Redux 替换官网的了,计数器的性能也是反对了。然而上面还想讲一下 React-Redux 是怎么保障组件的更新程序的,因为源码中很多代码都是在解决这个。

保障组件更新程序

后面咱们的 Counter 组件应用 connect 连贯了 redux store,如果他上面还有个子组件也连贯到了redux store,咱们就要思考他们的回调的执行程序的问题了。咱们晓得 React 是单向数据流的,参数都是由父组件传给子组件的,当初引入了Redux,即便父组件和子组件都援用了同一个变量count,然而子组件齐全能够不从父组件拿这个参数,而是间接从Redux 拿,这样就突破了 React 原本的数据流向。在 父 -> 子 这种单向数据流中,如果他们的一个专用变量变动了,必定是父组件先更新,而后参数传给子组件再更新,然而在 Redux 里,数据变成了 Redux -> 父,Redux -> 子齐全能够依据 Redux 的数据进行独立更新,而不能齐全保障父级先更新,子级再更新的流程。所以 React-Redux 花了不少功夫来手动保障这个更新程序,React-Redux保障这个更新程序的计划是在 redux store 外,再独自创立一个监听者类Subscription

  1. Subscription负责解决所有的 state 变动的回调
  2. 如果以后连贯 redux 的组件是第一个连贯 redux 的组件,也就是说他是连贯 redux 的根组件,他的 state 回调间接注册到 redux store;同时新建一个Subscription 实例 subscription 通过 context 传递给子级。
  3. 如果以后连贯 redux 的组件不是连贯 redux 的根组件,也就是说他下面有组件曾经注册到 redux store 了,那么他能够拿到下面通过 context 传下来的 subscription,源码外面这个变量叫parentSub,那以后组件的更新回调就注册到parentSub 上。同时再新建一个 Subscription 实例,代替 context 上的 subscription,持续往下传,也就是说他的子组件的回调会注册到以后subscription 上。
  4. state 变动了,根组件注册到 redux store 上的回调会执行更新根组件,同时根组件须要手动执行子组件的回调,子组件回调执行会触发子组件更新,而后子组件再执行本人 subscription 上注册的回调,触发孙子组件更新,孙子组件再调用注册到本人 subscription 上的回调。。。这样就实现了从根组件开始,一层一层更新子组件的目标,保障了 父 -> 子 这样的更新程序。

Subscription

所以咱们先新建一个 Subscription 类:

export default class Subscription {constructor(store, parentSub) {
    this.store = store
    this.parentSub = parentSub
    this.listeners = [];        // 源码 listeners 是用链表实现的,我这里简略解决,间接数组了

    this.handleChangeWrapper = this.handleChangeWrapper.bind(this)
  }

  // 子组件注册回调到 Subscription 上
  addNestedSub(listener) {this.listeners.push(listener)
  }

  // 执行子组件的回调
  notifyNestedSubs() {
    const length = this.listeners.length;
    for(let i = 0; i < length; i++) {const callback = this.listeners[i];
      callback();}
  }

  // 回调函数的包装
  handleChangeWrapper() {if (this.onStateChange) {this.onStateChange()
    }
  }

  // 注册回调的函数
  // 如果 parentSub 有值,就将回调注册到 parentSub 上
  // 如果 parentSub 没值,那以后组件就是根组件,回调注册到 redux store 上
  trySubscribe() {
      this.parentSub
        ? this.parentSub.addNestedSub(this.handleChangeWrapper)
        : this.store.subscribe(this.handleChangeWrapper)
  }
}

Subscription对应的源码看这里。

革新Provider

而后在咱们后面本人实现的 React-Redux 外面,咱们的根组件始终是 Provider,所以Provider 须要实例化一个 Subscription 并放到 context 上,而且每次 state 更新的时候须要手动调用子组件回调,代码革新如下:

import React, {useMemo, useEffect} from 'react';
import ReactReduxContext from './Context';
import Subscription from './Subscription';

function Provider(props) {const {store, children} = props;

  // 这是要传递的 context
  // 外面放入 store 和 subscription 实例
  const contextValue = useMemo(() => {const subscription = new Subscription(store)
    // 注册回调为告诉子组件,这样就能够开始层级告诉了
    subscription.onStateChange = subscription.notifyNestedSubs
    return {
      store,
      subscription
    }
  }, [store])

  // 拿到之前的 state 值
  const previousState = useMemo(() => store.getState(), [store])

  // 每次 contextValue 或者 previousState 变动的时候
  // 用 notifyNestedSubs 告诉子组件
  useEffect(() => {const { subscription} = contextValue;
    subscription.trySubscribe()

    if (previousState !== store.getState()) {subscription.notifyNestedSubs()
    }
  }, [contextValue, previousState])

  // 返回 ReactReduxContext 包裹的组件,传入 contextValue
  // 外面的内容就间接是 children,咱们不动他
  return (<ReactReduxContext.Provider value={contextValue}>
      {children}
    </ReactReduxContext.Provider>
  )
}

export default Provider;

革新connect

有了 Subscription 类,connect就不能间接注册到 store 了,而是应该注册到父级 subscription 上,更新的时候除了更新本人还要告诉子组件更新。在渲染包裹的组件时,也不能间接渲染了,而是应该再次应用 Context.Provider 包裹下,传入批改过的 contextValue,这个contextValue 外面的 subscription 应该替换为本人的。革新后代码如下:

import React, {useContext, useRef, useLayoutEffect, useReducer} from 'react';
import ReactReduxContext from './Context';
import shallowEqual from './shallowEqual';
import Subscription from './Subscription';

function storeStateUpdatesReducer(count) {return count + 1;}

function connect(mapStateToProps = () => {}, 
  mapDispatchToProps = () => {}
  ) {function childPropsSelector(store, wrapperProps) {const state = store.getState();   // 拿到 state

    // 执行 mapStateToProps 和 mapDispatchToProps
    const stateProps = mapStateToProps(state);
    const dispatchProps = mapDispatchToProps(store.dispatch);

    return Object.assign({}, stateProps, dispatchProps, wrapperProps);
  }

  return function connectHOC(WrappedComponent) {function ConnectFunction(props) {const { ...wrapperProps} = props;

      const contextValue = useContext(ReactReduxContext);

      const {store, subscription: parentSub} = contextValue;  // 解构出 store 和 parentSub
      
      const actualChildProps = childPropsSelector(store, wrapperProps);

      const lastChildProps = useRef();
      useLayoutEffect(() => {lastChildProps.current = actualChildProps;}, [actualChildProps]);

      const [
        ,
        forceComponentUpdateDispatch
      ] = useReducer(storeStateUpdatesReducer, 0)

      // 新建一个 subscription 实例
      const subscription = new Subscription(store, parentSub);

      // state 回调抽出来成为一个办法
      const checkForUpdates = () => {const newChildProps = childPropsSelector(store, wrapperProps);
        // 如果参数变了,记录新的值到 lastChildProps 上
        // 并且强制更新以后组件
        if(!shallowEqual(newChildProps, lastChildProps.current)) {
          lastChildProps.current = newChildProps;

          // 须要一个 API 来强制更新以后组件
          forceComponentUpdateDispatch();

          // 而后告诉子级更新
          subscription.notifyNestedSubs();}
      };

      // 应用 subscription 注册回调
      subscription.onStateChange = checkForUpdates;
      subscription.trySubscribe();

      // 批改传给子级的 context
      // 将 subscription 替换为本人的
      const overriddenContextValue = {
        ...contextValue,
        subscription
      }

      // 渲染 WrappedComponent
      // 再次应用 ReactReduxContext 包裹,传入批改过的 context
      return (<ReactReduxContext.Provider value={overriddenContextValue}>
          <WrappedComponent {...actualChildProps} />
        </ReactReduxContext.Provider>
      )
    }

    return ConnectFunction;
  }
}

export default connect;

到这里咱们的 React-Redux 就实现了,跑起来的成果跟官网的成果一样,残缺代码曾经上传 GitHub 了:https://github.com/dennis-jiang/Front-End-Knowledges/tree/master/Examples/React/react-redux

上面咱们再来总结下 React-Redux 的外围原理。

总结

  1. React-Redux是连贯 ReactRedux的库,同时应用了 ReactRedux的 API。
  2. React-Redux次要是应用了 Reactcontext api来传递 Reduxstore
  3. Provider的作用是接管 Redux store 并将它放到 context 上传递上来。
  4. connect的作用是从 Redux store 中选取须要的属性传递给包裹的组件。
  5. connect会本人判断是否须要更新,判断的根据是须要的 state 是否曾经变动了。
  6. connect在判断是否变动的时候应用的是浅比拟,也就是只比拟一层,所以在 mapStateToPropsmapDispatchToProps中不要反回多层嵌套的对象。
  7. 为了解决父组件和子组件各自独立依赖 Redux,毁坏了React父级 -> 子级 的更新流程,React-Redux应用 Subscription 类本人治理了一套告诉流程。
  8. 只有连贯到 Redux 最顶级的组件才会间接注册到 Redux store,其余子组件都会注册到最近父组件的subscription 实例上。
  9. 告诉的时候从根组件开始顺次告诉本人的子组件,子组件接管到告诉的时候,先更新本人再告诉本人的子组件。

参考资料

官网文档:https://react-redux.js.org/

GitHub 源码:https://github.com/reduxjs/react-redux/

文章的最初,感激你破费贵重的工夫浏览本文,如果本文给了你一点点帮忙或者启发,请不要悭吝你的赞和 GitHub 小星星,你的反对是作者继续创作的能源。

作者博文 GitHub 我的项目地址:https://github.com/dennis-jiang/Front-End-Knowledges

退出移动版