1、demo 解析
2、新建一个react项目
3、项目大概这个样子
4、先给自己建立一个Store库,这就是你redux数据的仓库了
store文件夹下有两个文件,
(1)reducers,把你各个页面的reducer汇合起来,给他们起不同的好听的名字,
我这里只有一个home页面
import { combineReducers } from 'redux'
import home from 'pages/home/reducer'
export default combineReducers({
home
})
(2)另一个文件是index.js
主要是用来创建你的库,创建库的时候我这里用到了两个参数而且还引入了一个中间件
没有中间件的Redux的过程是:action -> reducer,
而有了中间件的过程就是action -> middleware -> reducer,
使用中间件我们可以对action也就是对dispatch方法进行装饰,
我们可以用它来实现异步action、打印日志、错误报告等功能。
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import reducers from './reducers'
const store = createStore(reducers, applyMiddleware(thunk))
export default store
这时候你可以回头去看看上面对redux-thunk源码的解析,
你会发现这样包装后的dispatch非常可爱
当你在action中需要一个异步操作,并需要在回调中改变state的状态的时候,这就是一个绝佳的解决方案。
5、给你的组件注入这个库
在index.html里
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux'
import store from './store'
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
我们可以参照源码
//这里需要传store所以我们使用Provider的时候把store传入//那么我们引入了Provider它为我们做了什么呢?
export function createProvider(storeKey = 'store') {
const subscriptionKey = `${storeKey}Subscription`
class Provider extends Component { //将外部的store对象放入context对象中,使子孙组件上的connect可以直接访问到context对象中的store。
getChildContext() {
return { [storeKey]: this[storeKey], [subscriptionKey]: null }
}
//constructor是Provider初始化时,用于获取props的store对象
constructor(props, context) {
super(props, context)
this[storeKey] = props.store;
}
//首先,它把它引入的内容全部变成它的子级元素, //并且由于它处于整个index.html的最外层 //所以被它包裹的每一个元素都可以接收redux的store数据作为props
render() {
return Children.only(this.props.children) //this.props.children用于获取当前组件的所有子组件 //children.only表示用于获取仅有的一个子组件,没有或者超过一个均会报错. //所以注意: 确保Provider组件的直接子级为单个封闭元素,切勿多个组件平行放置 //引申问题:当这个项目需要用到router时该怎么办?把router包在倒数第二层,Provider在最外层
}
}
if (process.env.NODE_ENV !== 'production') {
Provider.prototype.componentWillReceiveProps = function (nextProps) {
if (this[storeKey] !== nextProps.store) {
warnAboutReceivingStore()
}
}
}
Provider.propTypes = {
store: storeShape.isRequired,
children: PropTypes.element.isRequired,
}
Provider.childContextTypes = {
[storeKey]: storeShape.isRequired,
[subscriptionKey]: subscriptionShape,
}
return Provider
}
export default createProvider()
6、page下的home页面有三个文件
actionTypes.js
export const GET_HOME_DATA = 'home/get_home_data'
你起什么名字都可以,只要不重复,你开心就好了。
这个名字贯穿了一条修改路线,你会发现接下来你的actionCreator.js和你的reducer.js里都用到了这个名字,不同的名字对应不同的数据操作,记号它,为了便于记号它,我为它专门设置了自己的actionType.js
actionCreator.js
import { GET_HOME_DATA } from './actionTypes'
export const loadHomeDataSync = (home) => {
return {
type: GET_HOME_DATA,
home
}
}
//先异步获取数据,为了避免麻烦我这里用mock数据代替了//再同步返回获取到的数据
export const loadHomeDataAsync = (dispatch) => {
return () => {
fetch('/mock/data.json')
.then(response => response.json())
.then(result => {
dispatch(loadHomeDataSync(result.data))
})
}
}
reducer.js
//给你要用的数据设置初值,并且当新的数据来了以后,对数据做你想要的处理
//我这里是当原数据为空,state为新数据,原数据有的话,和新数据进行合并返回一个新的state
import { GET_HOME_DATA } from './actionTypes'
const defaultState = {
home:null
}
export default (state=defaultState, action) => {
if (action.type === GET_HOME_DATA) {
if(!!state.home){
return {
home: [...state.home,...action.home]
}
}
else{
return {
...state,
home: [...action.home]
}
}
}
return state
}
7、page下的view下的Home.js
import React, { Component } from 'react';
import { loadHomeDataAsync } from '../actionCreator'
//connect作用:连接React组件与 Redux store
import { connect } from 'react-redux'
const mapState = (state) => {
return {
home: state.home.home
}
}
const mapDispatch = (dispatch) => {
return {
loadCategories () {
dispatch(loadHomeDataAsync(dispatch))
}
}
}
class Home extends Component {
componentDidMount(){
this.props.loadCategories() //在这里调用,当然,你想在哪调用都可以
}
render() {
console.log(this.props.home)
return (
<div>home</div>
);
}
}
export default connect(mapState,mapDispatch)(Home);//记得在这里把他们connect起来
那么connect他究竟为我们做了什么呢?
为什么connect后面跟两个括号?
它的基础作用是:
a、从context里获取store
b、在componentWillMount 里通过mapStateToProps获取stateProp的值
c、在componentWillMount 里通过mapDispatchToProps获取dispatchProps的值
d、在componentWillMount 里订阅store的变化
e、将获得的stateProp,dispatchProps,还有自身的props合成一个props传给下面的组件
源码参考:
export function createConnect({
connectHOC = connectAdvanced,
mapStateToPropsFactories = defaultMapStateToPropsFactories,
mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
mergePropsFactories = defaultMergePropsFactories,
selectorFactory = defaultSelectorFactory
} = {}) {
return function connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
{
pure = true,
areStatesEqual = strictEqual,
areOwnPropsEqual = shallowEqual,
areStatePropsEqual = shallowEqual,
areMergedPropsEqual = shallowEqual,
...extraOptions
} = {}
) {
const initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps')
const initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps')
const initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps')
return connectHOC(selectorFactory, {
// used in error messages
methodName: 'connect',
// used to compute Connect's displayName from the wrapped component's displayName.
getDisplayName: name => `Connect(${name})`,
// if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
shouldHandleStateChanges: Boolean(mapStateToProps),
// passed through to selectorFactory
initMapStateToProps,
initMapDispatchToProps,
initMergeProps,
pure,
areStatesEqual,
areOwnPropsEqual,
areStatePropsEqual,
areMergedPropsEqual,
// any extra options args can override defaults of connect or connectAdvanced
...extraOptions
})
}
} connect接收四个参数:mapStateToProps,mapDispatchToProps,mergeProps,optipons返回:一个注入了 state 和 action creator 的 React 组件
mapStateToProps
传入:state,ownProps 输出:stateProps
mapDispatchToProps
这个非常关键,如果定义了这个参数,就会监听redux store的变化,没有的话,就不会。 该回调函数必须返回一个纯对象,这个对象会与组件的 props 合并。 同时,如果指定了第二个ownProps,这个参数的值为传入到组件的props,只要组件接受到新的props,mapStateToProps也会被调用
mergeProps(function)
stateProps,dispatchProps,自身的props将传入到这个函数中。 默认是Object.assign({}, ownProps, stateProps, dispatchProps)
完整:
connect(mapStateToProps, mapDispatchToProps, mergeProps)(MyComponent)