浅探 react-redux

461 阅读2分钟

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

说到状态管理,可以提到的库有 vuex、redux、mobx 等,今天就来水一水 react 使用 redux 时常用到的库 react-redux。

react-redux,源码见 react-redux仓库。我看的版本是 8.0.0-alpha.1,这是一个用 typescript 和 react hook 编写的整合 redux 到 react 的库。src 目录如下:

src
├── alternate-renderers.ts
├── components
│   ├── Context.ts
│   ├── Provider.tsx
│   └── connect.tsx
├── connect
│   ├── mapDispatchToProps.ts
│   ├── mapStateToProps.ts
│   ├── mergeProps.ts
│   ├── selectorFactory.ts
│   ├── verifySubselectors.ts
│   └── wrapMapToProps.ts
├── exports.ts
├── hooks
│   ├── useDispatch.ts
│   ├── useReduxContext.ts
│   ├── useSelector.ts
│   └── useStore.ts
├── index.ts
├── types.ts
└── utils
    ├── Subscription.ts
    ├── batch.ts
    ├── bindActionCreators.ts
    ├── isPlainObject.ts
    ├── reactBatchedUpdates.native.ts
    ├── reactBatchedUpdates.ts
    ├── shallowEqual.ts
    ├── useIsomorphicLayoutEffect.native.ts
    ├── useIsomorphicLayoutEffect.ts
    ├── verifyPlainObject.ts
    └── warning.ts

它的 connect 函数的特别特别特别的函数重载列表和特别特别特别的类型给我带来了极大的心智负担,让我浏览完完之后眉头一皱,啥都没懂,所以跳过这个不谈,看看我们熟悉的 hooks, 这几个文件的代码都不长,可以水一下。

  • useStore 是用来创建状态管理用到的 store 的,通过 react 的 useContext消费了Context.ts 中创建的一个 ReactReduxContext, 该 context 有 store 和 subscription 两个字段;
  • useDispatch就是取出 store 中的 dispatch 函数;
  • useReduxContext也是消费ReactReduxContext的
  • useSelector的作用大概是传入 selector 函数选取 store 中符合条件的 state。

再看看 components 中的 Provider.ts,作用就是提供ReactReduxContext 对应的Provider组件。组件会根据传入的 store 创建 subscription,,将 store 和 subscription 作为一个 contextValue 传入ReactReduxContext.Provider 的 value 中。这也就可以理解我们在用 react-redux 的时候会出现以下的代码了。通过在根组件外面包裹 Provider,从而使整个应用所有组件都能访问到 store 了。

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import reducer from './reducers'
import App from './components/App'

let store = createStore(reducer)

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

util 文件夹的需要重点关注的是 Subscription.ts和bindActionCreators.ts 前者的作用就是创建订阅者对象,接口如下所示:

interface Subscription {
  addNestedSub: (listener: VoidFunc) => VoidFunc
  notifyNestedSubs: VoidFunc
  handleChangeWrapper: VoidFunc
  isSubscribed: () => boolean
  onStateChange?: VoidFunc | null
  trySubscribe: VoidFunc
  tryUnsubscribe: VoidFunc
  getListeners: () => ListenerCollection
}

该文件还有一个叫 createSubscription 的函数,接收 store 和父 Subscription 两个参数,返回新的 Subscription 对象,addNestedSub的主要作用是给Subscription 对象添加一个 listener 回调,当执行notifyNestedSubs 函数时,就会执行该对象所有的 listener 回调。第一次调用addNestedSub的时候,还会额外地给父 Subscription 调用一次 addNestedSub,添加的 listener 回调就是子 Subscription 的 handleChangeWrapper,该函数的内容就是当子 Subscription 定义了onStateChange函数的时候就会自动执行该函数,添加完这个 listener 回调,isSubscribe 就会变成 true。

剩下的 connect 文件夹下的代码和 component 文件夹下的connect.tsx 就不怎么看得懂了,行文暂时到此,给自己挖个坑,等下次研究完 redux 的源码再回过头来补更。