其实不想看react-redux、redux的源码,平时不太喜欢用,尤其是简单的页面,然而总是会遇到复杂点的页面的,所以还是要了解下,搜索了下,讲react-redux、redux的文章很多,但是,好像都不太能理解,可能是风格不太一样,没办法还是只能自己看,看了就记录下吧。
只讲最简单的使用涉及的东西,目前没怎么用中间件什么的。react-redux版本6.0.1,redux版本4.0.1
使用的例子如下:
main.jsx文件
import ReactDOM from "react-dom";
import React from "react";
import Comp1 from "./comp1";
import {createStore} from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './reducers';
class App extends React.Component {
render() {
return (
);
}
}
const store = createStore(rootReducer, {
activeTab: 0,
});
ReactDOM.render(
, document.getElementById("app"));
=====
comp1.jsx文件
import React, { Component } from "react";
import { connect } from 'react-redux';
import { changeTab } from './actions';
class Comp1 extends Component {
render() {
const {activeTab, changeTab} = this.props;
return (
{activeTab}
);
}
}
const mapStateToProps = (state, ownProps) => ({
activeTab: state.activeTab,
});
const mapDispatchToProps = (dispatch, ownProps) => ({
changeTab: (val) => {
dispatch(changeTab(val))
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Comp1);
=====
actions.js文件
export const changeTab = (val) => ({
type: 'change_tab',
activeTab: val
})
=====
reducers.js文件
export default (state = [], action) => {
switch (action.type) {
case 'change_tab':
return {...state, activeTab: action.activeTab};
default:
return state;
}
};
就是一个main里面渲染了一个子组件,子组件渲染了一个p标签和一个按钮,初始化activeTab等于0,按一下按钮变成了33。看一下整个过程涉及了哪些东西,首先是redux里的createStore,然后是react-redux的Provider和connect,没了,另外都是我们自己写的东西了。
先来看一眼createStore干了啥,打开redux/src/createStore.js看,createStore接收三个参数reducer, preloadedState, enhancer,最后一个不管它,第一个是我们自己写的reducer,第二个是初始化的时候的state。先翻到createStore函数的最后,看到
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
其中getState和replaceReducer比较简单,不说了,看subscribe这个名字应该能看出来这就是个发布订阅,subscribe是注册监听事件的函数,里面有这么一句nextListeners.push(listener)
,dispatch是调用reducer,并执行监听事件的地方,可以在里面看到
currentState = currentReducer(currentState, action)
...省略一些代码
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
看到这里你会问,是谁调用了subscribe呢?dispatch又是谁调用的呢?不出意外就是react-redux了
我们打开react-redux/src/components/Provider.js来看,发现Provider是个react组件,看Provider做了什么
constructor(props) {
super(props)
const { store } = props // 这个store就是我们用redux的createStore生成的那个store
this.state = {
storeState: store.getState(), // 看一眼
store
}
}
componentDidMount() {
this._isMounted = true
this.subscribe() // 看一眼
}
componentWillUnmount() {
if (this.unsubscribe) this.unsubscribe() // 看一眼
this._isMounted = false
}
componentDidUpdate(prevProps) {
if (this.props.store !== prevProps.store) {
if (this.unsubscribe) this.unsubscribe() // 看一眼
this.subscribe() // 看一眼
}
}
...省略一些代码
render() {
const Context = this.props.context || ReactReduxContext
return (
{this.props.children}
)
}
构造函数和生命周期一看,基本上知道Provider做了什么事情了吧,redux的createStore里的subscribe函数就是Provider调用的,那么再来看this.subscribe函数干了啥
subscribe() {
const { store } = this.props
this.unsubscribe = store.subscribe(() => { // 调用redux的createStore里的subscribe函数了
const newStoreState = store.getState()
if (!this._isMounted) {
return
}
this.setState(providerState => {
// If the value is the same, skip the unnecessary state update.
if (providerState.storeState === newStoreState) {
return null
}
return { storeState: newStoreState }
})
})
// Actions might have been dispatched between render and mount - handle those 看一眼这个注释
const postMountStoreState = store.getState()
if (postMountStoreState !== this.state.storeState) {
this.setState({ storeState: postMountStoreState })
}
}
还剩最后一个connect函数,就是调用dispatch的地方,这个部分怎么说呢,我还没看懂哈哈哈,比较复杂,我们的例子里,mapDispatchToProps的参数dispatch其实就是redux的creatStore里的那个dispatch,感兴趣的同学可以深究,我这里就不继续了(逃走),实在不好意思,不过我们的流程已经通了,嗯,很好(?)。
简单总结一下:redux就是个发布订阅,提供了subscribe和dispatch,react-redux在Provider里进行subscribe,connect的参数mapDispatchToProps里的dispatch就是redux的dispatch,用react的context API来实现更新机制(这部分上面没提到,也在connect相关的代码里,自己看?)