前面一篇文章已经讨论了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,下面让我们分别来实现它。
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的实现。
回顾下上面的示例
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的全部实现。