# React Redux 源码简单分析

简单的使用示例:

```javascript
import { Provider, connect } from './react-redux/src'
import { createStore } from './redux/src'
import rootReducer from './reducers'

const store = createStore(rootReducer)

function Child(props) {
  return <div>
    <div>name: {props.name}</div>
    <div>age: {props.age}</div>
    <button onClick={() => props.dispatch({
        type: 'CHANGE_NAME',
        data: Math.random()
      })}
    >
      change name
    </button>
    <button onClick={() => props.dispatch({
        type: 'CHANGE_AGE',
        data: Math.random()
      })}
    >
      change age
    </button>
  </div>
}

const mapStateToProps = state => {
  return {
    name: state.name,
    age: state.age
  }
}

const mapDispatchToProps = dispatch => ({
  onClick: () => dispatch()
})

const ConnectChild = connect(
  mapStateToProps,
  // mapDispatchToProps
)(Child)

function Demo() {
  return <Provider store={store}>
    <ConnectChild />
  </Provider>
}
export default Demo;
```

## 1. Redux 源码

源码：<https://github.com/reduxjs/redux/tree/v4.1.0>

```javascript
/Users/songjp/fe/mime/workshop/packages/react-basic/src/react-redux-demo/redux/src
├── applyMiddleware.js
├── bindActionCreators.js
├── combineReducers.js
├── compose.js
├── createStore.js
├── index.js
└── utils
   ├── actionTypes.js
   ├── formatProdErrorMessage.js
   ├── isPlainObject.js
   ├── kindOf.js
   ├── symbol-observable.js
   └── warning.js
```

*index.js* 导出了以下内容:

```javascript
export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose
}
```

### 1.1 createStore

源码简化版:

```javascript
import ActionTypes from './utils/actionTypes'

export default function createStore(reducer, preloadedState, enhancer) {
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners

  function getState() {
    return currentState
  }

  function subscribe(listener) {
    nextListeners.push(listener)

    return function unsubscribe() {
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

  function dispatch(action) {
    currentState = currentReducer(currentState, action)
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

  dispatch({ type: ActionTypes.INIT })

  return {
    dispatch,
    subscribe,
    getState,
  }
}
```

1. createStore 创建了一个 store 对象，含有 `dispatch`, `subscribe`, `getState` 等方法
2. 大致原理是，内部通过闭包维护一个 `currentState` 属性，`nextListeners` 属性。`nextListeners` 对应 **观察者模式** 中的观察者列表。
3. `subscribe` 方法可以往 `nextListeners` 中添加观察者
4. 当调用 `dispatch` 方法是，会执行 `currentState = currentReducer(currentState, action)` 改变当前的 `currentState` 属性。之后会通知到所有的观察者去执行。
5. 而 `getState` 方法可以获取最新的 `currentState` 方法。

## 2. React-Redux 源码

源码地址: <https://github.com/reduxjs/react-redux/tree/v6.0.0>

*在 React-Redux 7.1 版本之后，引入 hooks 写法，老实说，源码可阅读性有点下降(是的，我没看懂...)，因此，我们还是看旧版本的代码吧，大致思想应该是一致的*

### 2.1 Provider 源码

*源码简化版*:

```javascript
class Provider extends Component {
  constructor(props) {
    const { store } = props
    this.state = {
      storeState: store.getState(),
      store
    }
  }

  componentDidMount() {
    this.subscribe()
  }

  subscribe() {
    const { store } = this.props

    store.subscribe(() => {
      const newStoreState = store.getState()
      this.setState(providerState => {
        // If the value is the same, skip the unnecessary state update.
        if (providerState.storeState === newStoreState) {
          return null
        }
        return { storeState: newStoreState }
      })
    })
  }

  render() {
    const Context = this.props.context || ReactReduxContext

    return (
      <Context.Provider value={this.state}>
        {this.props.children}
      </Context.Provider>
    )
  }
}
```

1. Provider 内部有 `storeState` 和 `store` 两个 state
2. 在 componentDidMount 时调用 `store.subscribe()`, 内部会重新 `setState({ storeState })`. 即当我们在子组件调用 dispatch 方法时, 会更新 Provider 组件的 storeState 状态。
3. Provider 再通过 Context 将自己的 state 传递到子组件中

### 2.2 connectAdvanced.js 源码

```javascript
class Connect extends OuterBaseComponent {
  constructor(props) {
    super(props)
    invariant(
      forwardRef ? !props.wrapperProps[storeKey] : !props[storeKey],
      'Passing redux store in props has been removed and does not do anything. ' +
        customStoreWarningMessage
    )
    this.selectDerivedProps = makeDerivedPropsSelector()
    this.selectChildElement = makeChildElementSelector()
    this.renderWrappedComponent = this.renderWrappedComponent.bind(this)
  }

  renderWrappedComponent(value) {
    const { storeState, store } = value

    let wrapperProps = this.props
    let forwardedRef

    if (forwardRef) {
      wrapperProps = this.props.wrapperProps
      forwardedRef = this.props.forwardedRef
    }

    // 在这里，会根据 matchStateToProps 以及 matchDispatchToProps
    // 计算出最终需要传递给子组件的 props, 比如在我们的示例中是 { name, age, dispatch }
    // 不过 selectDerivedProps 这个方法确实有点绕，代码看晕了
    let derivedProps = this.selectDerivedProps(
      storeState,
      wrapperProps,
      store
    )
    return this.selectChildElement(derivedProps, forwardedRef)
  }

  render() {
    const ContextToUse = this.props.context || Context

    return (
      <ContextToUse.Consumer>
        {this.renderWrappedComponent}
      </ContextToUse.Consumer>
    )
  }
}
```

* 被 `connect()(Component)` 包裹的组件, 会通过 `selectDerivedProps(storeState, store)` 从 storeState 中得到你需要的属性 derivedProps (通过计算你的 **mapStateTopProps** 和 **mapDispatchToState** 得出)

## 3. 原理总结

1. 在 Redux 库中，createStore() 返回 store 对象，对象上有 `{ getState, dispatch, getState }` 等属性 ，该对象传给 React-Redux 的 Provider 组件
2. React-Redux 中的 Provider 组件中通过 store.subscribe 监听 store 的**状态数据**变化。每次**数据**变化后都会把**状态数据**用 Context API 传递到子组件
3. 当调用 dispatch 方法时，在 Redux 中会通过 `currentState = currentReducer(currentState, action)` 也就是"过一遍" reducer 方法，得到最新的**状态数据**
4. 此时子组件会得到最新的 state 信息，通过 `selectDerivedProps(storeState, store)` 来得到最终的 props，进行渲染。另外，如果发现从 store 中接受到的 props 没有发生变化，则不会重新渲染。

### 3.1 流程

Redux 中的三个概念:

1. dispatch
2. reducer
3. state

至于 Action Creator, 实际上就是为了便捷的创建 action，**有更好，没有也行**。

```javascript
用户的交互，触发 dispatch(aciton)  => 通过 reducer(currentState, action) 进行计算，得到最新的 state => 当 state 发生变化时，connect 中会重新计算需要赋值给子组件的 props，并重新渲染子组件
```

## 4. TODO

整理 compose，applyMiddleware，combineReducers，以及中间件的一个原理


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yes-1-am.gitbook.io/blog/react-kai-fa-shi-jian/reactredux-yuan-ma-jian-dan-fen-xi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
