1、redux
1.1)Action Creator
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
----
import * as types from '../action-types';
//actionCreator 创建action的函数
export default {
increment(){
return {type:types.INCREMENT}
},
decrement(){
return {type:types.DECREMENT}
}
}
复制代码
看着写了这么多,其实就是为了拿到字符串INCREMENT
和DECREMENT
。
1.2)reducer
reducer是一个纯函数,相同的输入有相同的输出,不同的输入得到不同的输出。它必须遵守以下几点
- 不得改写参数
- 不能调用系统 I/O 的API
- 不能调用Date.now()或者Math.random()等不纯的方法,因为每次会得到不一样的结果
reducer中不能改变state,在保证旧的状态不变的情况下,返回一个全新的状态
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
let initState = { number: 0 }
export default function (state = initState, action) {
switch (action.type) {
case INCREMENT:
return { number: state.number + 1 };
case DECREMENT:
return { number: state.number - 1 };
default:
return state;
}
}
复制代码
其实就相当于state变成了一个只是可读的状态,不可更改,返回了计算后的新的状态,原状态不变。
1.3)createStore
const createStore = (reducer) => {
let state;
let listeners = [];
const getState = () => state;// 因为dispatch的执行,拿到的是初始状态
//订阅,供外界订阅本仓库中状态的变化 ,如果状态变化 了会执行订阅的逻辑
const subscribe = (listener) => {
listeners.push(listener);
return () => {//subscribe的返回结果是一个unsubscribe
listeners = listeners.filter(l => l !== listener);
}
};
const dispatch = (action) => {
//接收新的动作后,通过 才状态 和新动作计算出新状态
state = reducer(state, action);
listeners.forEach(listener => listener());
};
dispatch({});
// 这里执行dispatch的原因是,为了运行reducer函数,最终获取到里面的初始 state,也就是说createStore()执行后,立马拿到初始状态。
return { getState, dispatch, subscribe };
};
复制代码
1.4)combinReducers
将两个reducer合并
function combineReducers(reducers) {
//返回合并后的reducer,这个函数将被传入createStore
return function (state = {}, action) {
let newState = {};
for (let attr in reducers) {
let reducer = reducers[attr];
newState[attr] = reducer(state[attr], action);
}
return newState;
}
}
export default combineReducers({
reducer1,
reducer2
});
复制代码
另外一种写法:
export default reducers => (state = {}, action) => Object.keys(reducers).reduce((currentState, key) => {
currentState[key] = reducers[key](state[key], action);
return currentState;
}, {});
复制代码
1.5)bindActionCreator
我们做下面一个操作
<button onClick={()=>store.dispatch(actions.increment())}>-</button>
复制代码
bindActionCreator
用来把action
和dispatch
绑定在一起,让书写变得更方便
<button onClick={newActions.increment}>-</button>
复制代码
这是bindActionCreators
的实现原理:
function bindActionCreators(actions,dispatch){
let newActions = {};
for(let attr in actions){
newActions[attr] = function(){
dispatch(actions[attr].apply(null,arguments));
}
}
return newActions;
}
复制代码
1.6) 我们可以来生成redux了
import createStore from './createStore';
import combineReducers from './combineReducers';
import bindActionCreators from './bindActionCreators';
export {
createStore,
combineReducers,
bindActionCreators
}
复制代码
二、react-redux
1、Provider
provider
的作用是让每个组件都拥有调用store
的能力
Provider
本身并没有做很多事情,只是把store
放在context
里罢了。实际上如果你用react-redux
,那么连接视图和数据层最好的办法是使用connect
函数。本质上Provider
就是给connect
提供store
用的。
//用来通过上下文对象向下层组件传递数据 store
import React,{Component} from 'react';
import propTypes from 'prop-types';
export default class Provider extends Component{
static childContextTypes = {
store:propTypes.object.isRequired
}
getChildContext(){
return {store:this.props.store};
}
render(){
return this.props.children;
}
}
复制代码
一般我们在跟组件上使用
const store = configureStore();
render(
<Provider store={store}>
<Root />
</Provider>,
document.getElementById('root')
);
复制代码
2、connect
React-Redux
提供connect
方法,用于从 UI
组件生成容器组件。connect
的意思,就是将这两种组件连起来。
作用:
-
(1)输入逻辑:外部的数据(即
state
对象)如何转换为UI
组件的参数 -
(2)输出逻辑:用户发出的动作如何变为
Action
对象,从UI
组件传出去
connect
的使用
import { connect } from 'react-redux'
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
复制代码
connect
有两个参数,分别是 mapStateToProps
,mapDispatchToProps
。
第一个参数负责输入逻辑,即将state
映射到 UI
组件的参数(props
),第二个参数负责输出逻辑,即将用户对UI
组件的操作映射成 Action
。
2.1 ) mapStateToProps
返回值,是一个对象.
从字面上理解,就是把状态(store
中的state
)映射成属性(组件中的props
)
const mapStateToProps = (state) => ( // 正常我们在react-redux中会这样书写
{number: state.number}
)
复制代码
2.2)mapDispatchToProps
mapDispatchToProps
是connect
函数的第二个参数,用来建立 UI
组件的参数到store.dispatch
方法的映射。也就是说,它定义了哪些用户的操作应该当作 Action
,传给 Store
。它可以是一个函数,也可以是一个对象。
如果mapDispatchToProps
是一个函数,会把dispatch
当做一个参数传进去。
const mapDispatchToProps = (dispatch) => {
return {
onClick: () => {
dispatch({
type: 'ADD'
});
}
};
}
// 返回了一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action。
复制代码
如果mapDispatchToProps
是一个对象,它的每个键名也是对应 UI 组件的同名参数,键值应该是一个函数,会被当作 Action creator ,返回的 Action 会由 Redux 自动发出。
bindActionCreators(mapDispatchToProps,this.store.dispatch)
复制代码
2.3) connect 的实现
在connect中实现了组件的更新操作。
import React,{Component} from 'react';
import {bindActionCreators} from 'redux';
import propTypes from 'prop-types';
export default function(mapStateToProps,mapDispatchToProps){
return function(WrapedComponent){
class ProxyComponent extends Component{
static contextTypes = {
store:propTypes.object
}
constructor(props,context){
super(props,context);
this.store = context.store;//这里的store是从Prodiver传过来的
this.state = mapStateToProps(this.store.getState());//{color:state.color}
}
componentWillMount(){
this.unsubscribe = this.store.subscribe(()=>{
this.setState(mapStateToProps(this.store.getState()));// 根据store中的state来更新视图。
});
}
componentWillUnmount(){
this.unsubscribe();
}
render(){
let actions= {};
if(typeof mapDispatchToProps == 'function'){
actions = mapDispatchToProps(this.store.disaptch);
}else if(typeof mapDispatchToProps == 'object'){
actions = bindActionCreators(mapDispatchToProps,this.store.dispatch);
}
return <WrapedComponent {...this.state} {...actions}/>
}
}
return ProxyComponent;
}
}
复制代码
2.4) 最后我们来生成react-redux
import connect from './connect';
import Provider from './Provider';
export {
connect,
Provider
}
复制代码