redux、react-redux源码学习

422 阅读4分钟

本文通过点击按钮实现count加减的简单案例,来实现状态管理。进一步学习了解 reduxreact-redux 的的源码实现过程。

基本代码

  1. 入口文件---index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
// import { Provider } from 'react-redux'
import { Provider } from './react-redux/ReactRedux'
import store from './store'

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
  1. app组件
import Counter from './Counter'

function App() {
  return (
    <div className="App">
      <Counter></Counter>
    </div>
  );
}
export default App;
  1. count.jsx文件
import React, { Component } from 'react';
// import { connect } from 'react-redux'
import { connect } from './react-redux/ReactRedux'
import actions from './store/action'

class Counter extends Component {
    // 发送异步action
     handleAdd = () => {
        this.props.dispatch((dispatch) => {
            setTimeout(() => {
                dispatch({type: 'ADD'})
            }, 1000)
        })
    }
    
    render() {
        console.log('this.props', this.props);
        return (
            <div>
                Counter
                {
                    this.props.state?<p>{this.props.state.number}</p>:null
                }
                {/* <p>{this.props.state.number}</p> */}
                
                {/* 当mapDispatchToProps为对象形式的时候,直接调用action中的add方法(返回的是
                {type: actionTypes.ADD}),react-redux内部会调用redux中的bindActionCreators方法
                将action包装成dispatch(action)的形式 */}
                <button onClick={this.props.add}>+</button>
                
                <button onClick={() => this.props.dispatch(actions.add())}>+</button>

                <button onClick={this.props.sendAdd}>+</button>
                {/* 执行count-- */}
                <button onClick={() => this.props.dispatch({type: 'SUB'})}>直接dispatch到reducer</button>
                {/* 执行异步操作 */}
                <button onClick={this.handleAdd}>异步+</button>
            </div>
        );
    }
}
let mapStateToProps = (state) => ({ state })

let mapDispatchToProps = (dispatch) => ({
    sendAdd: () => dispatch(actions.add()),
    dispatch  // 将dispatch暴露到this.props中
})

// mapDispatchToProps 写成是一个函数 
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

// mapDispatchToProps 写成是对象的形式
// export default connect(mapStateToProps, {...actions})(Counter);
  1. action-types.js -- 用于定义action常量
export const ADD = 'ADD'
export const SUB = 'SUB'
  1. action.js
import * as actionTypes from './action-type'
const actions = {
    add() {
        return {
            type: actionTypes.ADD
        }
    }
}
export default actions
  1. reducer.js
import * as actionTypes from './action-type'

const reducer = (state = {number: 0}, action) => {
    switch (action.type) {
        case actionTypes.ADD:
            return { number: state.number + 1 }
        case actionTypes.SUB:
            return { number: state.number - 1 }
        default:
            return state
    }
}

export default reducer
  1. store.js
// import { createStore, applyMiddleware } from 'redux'
import { createStore, applyMiddleware, logger, thunk } from '../redux/ReduxPage'

import reducer from './reducer'

const store  = createStore(reducer)

export default store

实现redux源码

// createStore 接收两个参数
export function createStore(reducer, enhancer) {
    
    let currentState = undefined
    let currentListeners = []
    // getState方法 返回currentState
    function getState() {
        return currentState

    }

    function dispatch(action) {
       // reducer 函数接收 currentState 和 action, 返回最新的state
        currentState = reducer(currentState, action)
        // 监听函数是一个数组
        currentListeners.map(listener => listener())
    }

    // 订阅 可以多次订阅
    function subscribe(listener) {
        // 每次订阅,把回调放入回调数组
        currentListeners.push(listener)
    }

    // 开始调用dispatch发送action,目的为了返回reducer定义的初始state的值
    dispatch({type: "@INIT/STATE"})
    
    if(enhancer) {
        // 借助中间件返回加强版的 dispatch,同时覆盖掉store中原来的dispatch. 
        // enhancer = applyMiddleware(thunk, logger)
        return enhancer(createStore)(reducer)
    }
    return {
        getState,
        dispatch,  // 经过中间件加强的dispatch
        subscribe
    }
}

// applyMiddleware(thunk, logger)(createStore)(reducer)
export function applyMiddleware(...middlewares) {
    return createStore => (...args) => {
        const store = createStore(...args)  // 基础版的store
        let dispatch = store.dispatch
        const middleApi = {
            getState: store.getState,
            dispatch
        }
        // middlewareChain:是将getState dispatch 注入到中间件中(thunk等)后中间件的数组
        const middlewareChain =  middlewares.map(middleware => middleware(middleApi))
        console.log('middlewareChain', middlewareChain)
        // 将多个中间件聚合成一个返回加强版的 dispatch 
        // dispatch 需要经过每个中间件的加强,返回最终版的dispatch,第一个中间件需要我们手动传入dispatch
        dispatch = compose(...middlewareChain)(dispatch);  
        return {
            ...store,
            dispatch
        }
    }
}

// 聚合函数当前函数的参数为上个函数返回的结果
function compose(...funcs) {
    if(funcs.length === 0) {
        return arg => arg;
    }
    if(funcs.length === 1) {
        return funcs[0];
    }
    return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

// 简单实现 logger 中间件
export function logger() {
    return dispatch => action => {
        console.log(action.type + '执行了')
        return dispatch(action)
    }
}

// 简单实现thunk中间件
export function thunk({dispatch, getState}) {
    return dispatch => action => {
        // action可以是对象可以是回调可以是函数
        if (typeof action === 'function') {
            console.log('thunk', dispatch)
            /* 因为在聚合 applyMiddleware 中调用的顺序是 logger thunk,
            同时compose()的结果是当前函数的参数是上一个函数的返回结果,
            所以dispatch是logger函数dispatch的结果。
             action => {
                console.log(action.type + '执行了');
                return dispatch(action);
            }
            */
            return action(dispatch, getState)
        }else{  
            return dispatch(action)
        }
    }
}

实现react-redux源码

import React, { Component } from 'react'
// import { bindActionCreators } from 'redux'

// 借助context,实现状态共享,创建上下文
const valueContext = React.createContext()

// 实现 connect 高阶组件
// connect(mapStateToProps, mapDispatchToProps)(Component)

export const connect = (
        mapStateToProps = state => state, 
        mapDispatchToProps
    ) => WarpedComponent => {
    return class extends Component {
        static contextType = valueContext
        constructor(props) {
            super(props) 
            this.state = {
                props: {}
            }
        }
        componentDidMount() {
           const { subscribe } = this.context;
           this.update()
           // 订阅
           subscribe(() => {
               this.update()
           })
        }
        // 
        update = () => {
            const { dispatch, getState } = this.context
            // 获取当前store中的state
            let stateProps = mapStateToProps(getState())
            let dispatchProps;
            if (typeof mapDispatchToProps === 'object') {
                // 如果传入的是一个对象的形式,会调用  redux 中bindActionCreators将action包装成 一个 dispatch(action)的形式
                dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
            }else if(typeof mapDispatchToProps === 'function') {
                // 如果是一个函数直接返回mapDispatchToProps函数执行返回的结果
                /*
                
                let mapDispatchToProps = (dispatch, ownPEops) => ({
                    sendAdd: () => dispatch(actions.add()),
                    dispatch
                })
                */
                // this.props 是 mapDispatchToProps 的第二参数 ownProps
                dispatchProps = mapDispatchToProps(dispatch, this.props)
            }else{
                // 默认,就是在connect中不传入第二个参数
                dispatchProps = {dispatch}
            }
            this.setState({
                props: {
                    ...stateProps,
                    ...dispatchProps
                }
            })
        }
        render() {
            console.log('context', this.context)
            // 注入共享的state和dispatch,返回高阶组件
            return (
                <WarpedComponent {...this.state.props}/>
            )
        }
    }
} 

// 实现Provider
export class Provider extends Component {
    render() {
        return <valueContext.Provider value={this.props.store}>{this.props.children}</valueContext.Provider>
    }
}


// 当mapDispatchToProps是一个对象的时候,{...action} ===> {add: ()=>({type: actionTypes.ASYNC_ADD })}
// 当在render 函数中直接调用 this.props.add() 是就会自动调用dispatch发送action

function bindActionCreator(creator, dispatch) {
    // creator ---> (...args)=>({type: actionTypes.ASYNC_ADD }) -- actionCreator中定义的add函数
    // dispatch 发送的action中包含type还有可能包含其他参数比如 payload
    return (...args) => dispatch(creator(...args))
}
// 将action中的多个action对象包装成 dispatch(action)类型
export function bindActionCreators(creators, dispatch) { 
    const obj = {}
    for (const key in creators) {
        obj[key] = bindActionCreator(creators[key], dispatch)
    }
    return obj
}

react-redux理解:

当我们使用connect(mapStateToProps, mapDispatchToProps)(ChildComponent)时,

  1. 定义用来获取state的函数mapStateToProps和用来获取dispatch方法的mapDispatchToProps。

  2. react-redux内部会调用mapStateToProps和mapDispatchToProps函数,并将state和dispatch方法传入到两个函数中,最后获取到两个函数的返回值(即state,dispatch),最最后将state、dispatch以父子组件传值的形式注入到ChildComponent的高阶组件中。

  3. 在connect()()返回的高阶组件中就可以获取到共享的state状态和dispatch方法。