react-redux的原理及简单实现

前面一篇文章已经讨论了redux的实现原理,如果没看过的小伙伴可以点击链接查看redux的原理及简单实现

下面让我们先看下在react中使用redux的例子:

import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware } from 'redux';
import { Provider, connect } from 'react-redux';
import thunk from 'redux-thunk';
import reducers from './reducer';
import { handleClick } from './action';


class App extends React.Component {
    render() {
        return 
    }
}

const mapStateToProps = state => state;
const mapDispatchToProps = { handleClick };

App = connect(mapStateToProps, mapDispatchToProps)(App);


const store = createStore(reducers, applyMiddleware(thunk));

ReactDOM.render(
    
        
    ,
    document.getElementById('root')
);

从例子中可以看到,从react-redux中引入的函数只有Provider和connect,下面让我们分别来实现它。

Provider的实现

react-redux实现store对象的传递的机制是借助了react中的Context,所以Provider其实就是声明了一个存有store对象的Context,让子组件可以获取到store对象,并添加订阅事件。下面就是简单的实现:

export class Provider extends React.Component {
    // 声明了context里store的类型
    static childContextTypes = {
        store: PropTypes.object
    }
    // 给context中的store赋值
    getChildContext() {
        return { store: this.props.store };
    }
    render() {
        return this.props.children;
    }
}

Provider的代码其实就是简单的context的使用,下面来看看connect的实现。

Connect的实现

回顾下上面的示例

const mapStateToProps = state => state;
const mapDispatchToProps = { handleClick };

App = connect(mapStateToProps, mapDispatchToProps)(App);

可以看出connect实际上是返回了两层嵌套函数,第二个参数为App,说明它是对App进行了一层包装,也就是常说的高阶组件。所以他的结构是下面这样的:

const connect = (mapStateToProps = state => state, mapDispatchToProps = {}) => WrapComponent => {
    return class ConnectComponent extends React.Component {
        render() {
            return (
                
            )
        }
    };
}

高阶组件的好处就是可以在内部类ConnectComponent进行额外的一些工作,就比如我们马上要用到的获取Provider提供的context的工作。

const connect = (mapStateToProps = state => state, mapDispatchToProps = {}) => WrapComponent => {
    return class ConnectComponent extends React.Component {
        // 这里用于获取Provider声明的store
        static contextTypes = {
            store: PropTypes.object
        }
        render() {
            // 可以通过this.context获取store
            const { store } = this.context;
            return (
                
            )
        }
    };
}

既然store已经获取到了,那么接下来的工作就是为当前组件向store中注册监听事件了,也就是store.subscribe()。实现代码如下:

const connect = (mapStateToProps = state => state, mapDispatchToProps = {}) => WrapComponent => {
    return class ConnectComponent extends React.Component {
        static contextTypes = {
            store: PropTypes.object
        }
        constructor(props, context) {
            super(props, context);
            // 用来保存mapStateToProps和mapDispatchToProps计算出来的值
            this.state = {
                props: {}
            };
        }
        componentDidMount() {
            const store = this.context.store;
            // 向store注册事件监听,当数据改变时调用this.update
            store.subscribe(() => this.update());
            // 这里先调用一次update,用来初始化this.state.props
            this.update();
        }
        update() {
            const store = this.context.store;
            let state = store.getState();
            // 调用mapStateToProps,计算出当前组件需要的state
            state = mapStateToProps(state);
            // 针对action,这里需要特殊处理下,返回一个对象。下面会介绍这个函数
            let actions = bindActionCreators(mapDispatchToProps, store.dispatch);
            this.setState({
                props: {
                    ...this.state.props,
                    ...state,
                    ...actions
                }
            });
        }
        render() {
            return (
                
            )
        }
    };
}

function bindActionCreators(mapDispatchToProps, dispatch) {
    return Object.keys(mapDispatchToProps).reduce((ret, key) => {
        ret[key] = (...args) => dispatch(mapDispatchToProps[key](...args));
        return ret;
    }, {});
}

从上面代码可以看出,我们在componentDidMount中向store注册了监听函数,并初始化调用了一次update。

componentDidMount() {
    const store = this.context.store;
    store.subscribe(() => this.update());
    this.update();
}

根据mapStateToProps和mapDispatchToProps计算props后,在render时加入到component中,就是下方的this.state.props。

render() {
    return (
        
    )
}

最后让我们来看看update中bindActionCreators的实现吧

let actions = bindActionCreators(mapDispatchToProps, store.dispatch);

在redux中,action都是如下格式

export function add() {
    return { type: 'add' };
}

而我们在redux中调用store.dispatch时,传入的是对象,如store.dispatch( { type: 'add' } )。这个参数实际上就是add这个函数调用的返回结果。

所以我们要做的其实就是用户在组件内部调用add这个方法时,实际上去调用store.dispatch(add())。下面就是这个逻辑的实现:

// let actions = bindActionCreators(mapDispatchToProps, store.dispatch);

function bindActionCreators(mapDispatchToProps, dispatch) {
    return Object.keys(mapDispatchToProps).reduce((ret, key) => {
        ret[key] = (...args) => dispatch(mapDispatchToProps[key](...args));
        return ret;
    }, {});
}

到此已完成react-redux的全部实现。

你可能感兴趣的:(js,react,redux,react-redux)