# react-redux实现

在这里看源代码 (opens new window)

其实 react-redux 主要就两个方法 Provider,connect


import React from 'react'
import PropTypes from 'prop-types'

//provider

export class Provider extends React.Component {
    static childContextTypes = {
        store: PropTypes.object.isRequired
    }
    getChildContext() {
        return { store: this.store }
    }
    constructor(props, context) {
        super(props, context)
        this.store = props.store
    }
    render() {
        return this.props.children
    }
}



//connect

function bindActionCreator(actionCreator, dispatch) {
    return (...args) => dispatch(actionCreator(...args));
}
function bindActionCreators(actionCreator, dispatch) {
    //actionCreators 可以是一个普通函数或者是一个对象
    if (typeof actionCreator === 'function') {
        //如果是函数,返回一个函数,调用时,dispatch 这个函数的返回值
        bindActionCreator(actionCreator, dispatch);
    } else if (typeof actionCreator === 'object') {
        //如果是一个对象,那么对象的每一项都要都要返回 bindActionCreator
        const boundActionCreators = {}
        for (let key in actionCreator) {
            boundActionCreators[key] = bindActionCreator(actionCreator[key], dispatch);
        }
        return boundActionCreators;
    }
}

export const connect = (
    mapStateToProps = state => ({}),
    mapDispatchToProps = {}
) => (WrapComponent) => {
    return class ConnectComponent extends React.Component {
        static contextTypes = {
            store: PropTypes.object
        }
        constructor(props, context) {
            super(props, context)
            this.state = {
                props: {}
            }
        }
        // 2 实现了mapStateToProps
        componentDidMount() {
            const { store } = this.context
            this.unsub = store.subscribe(() => this.update())
            this.update()
        }
        update() {
            const { store } = this.context
            // store.getState()这就是为什么mapStateToProps函数里面能拿到state 
            const stateProps = mapStateToProps(store.getState(), this.props)
            // 方法不能直接给,因为需要dispatch
            /**
              function addGun() {
                return { type: ADD_GUN }
              }
              直接执行addGun() 毫无意义
              要 addGun = () => store.dispatch(addGun()) 才有意义,其实就是把actionCreator包了一层
              bindActionCreators在手写redux api实现了
             */
            const dispatchProps = bindActionCreators(mapDispatchToProps, store.dispatch)
            // 注意state的顺序问题会覆盖

            //这里应该对比一下 props 是否发生变化
            this.setState({
                props: {
                    ...this.state.props,
                    ...stateProps,
                    ...dispatchProps,
                }
            })
        }
        componentWillUnmount() {
            //取消订阅
            this.unsub();
        }
        // 1
        render() {
            return <WrapComponent {...this.state.props}></WrapComponent>
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

# Provider

就是一个高阶组件,给整个项目添加了一个 store 的context

# connect

也是一个HOC,它拿到放在context上的store,然后订阅一个更新this.unsub = store.subscribe(() => this.update()) 当执行更新时,把store.getState() 获取state,传给 传进来的WrapComponent组件

至此,react-redux 完成,下面看两个常用的 中间件

# logger

export const logger = store => next => action => {
    if (typeof action !== 'function') {
        console.log('dispatching', action)
    }
    let result = next(action)
    console.log('next state', store.getState())
    return result
}
1
2
3
4
5
6
7
8

# redux-thunk

function createThunkMiddleware(extraArgument) {
    return ({ dispatch, getState }) => (next) => (action) => {
        if (typeof action === 'function') {
            return action(dispatch, getState, extraArgument);
        }

        return next(action);
    };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;
1
2
3
4
5
6
7
8
9
10
11
12
13
14