本文转载于:猿2048网站→https://www.mk2048.com/blog/blog.php?id=h12c0h0kcb
不能因为别人怀疑自己,但是可以通过别人启发自己 !
昨天有人让我把他当小白讲讲redux,我表示理出来的逻辑并不是很明确,他可能是在教我如何写文章吧,我对自己写的东西,并不是很负责,目的一直停留在增强自己的短时间记忆,我会安排时间将之前的文章做逻辑性梳理,当然先从这篇开始。
我们知道react组件传递是单向的,如果组件关系太远,或者没有关系,我们就会很麻烦,redux就是解决这个问题,他将数据存储到仓库,通过reducer派发action动作,来substrict
redux组成有5个
- createStore 创建仓库,接受reducer作为参数
- bindActionCreator 绑定store.dispatch和action 的关系
- combineReducers 合并多个reducers
- applyMiddleware 洋葱模型的中间件,介于dispatch和action之间,重写dispatch
- compose 整合多个中间件
createStore
主要导出getState,dispatch,subscribe三个方法,分别是获取仓库,派发动作和订阅事件。生成store,他包含所有数据,拿数据通过getState获取, 一个 State 对应一个 View。只要 State 相同,View 就相同。
其内部保存啦一颗状态树,这个状态树是任意类型的,当获取状态树的时候后会复制一份状态树吗,并导出,防止对状态树的恶意更改。
因此,只能通过派发dispatch更改状态,dispatch会调用reducer处理action 动作,reducer是个函数,他有state和action两个参数,作用是将老得state经过action处理后返回新的state,action是个有type属性的对象,来标示他对哪个状态进行改变
于此同时,当状态发生改变会触发subscribe订阅的监听事件
export default function createStore(reducer,initState,enchancer) {
if (enchancer) {
return enchancer(createStore)(reducer,initState);
}
//仓库内部保存了一颗状态树。可以是任意类型
let state;
let listeners=[];
function getState() {
return JSON.parse(JSON.stringify(state));
}
//组件可以派发动作给仓库
function dispatch(action) {
//调用reducer进行处理,获取老的state,计算出新的state
state=reducer(state,action);
//通知其他的组件
listeners.forEach(l=>l());
}
//如果说其他的组件需要订阅状态变化时间的话,
function subscribe(listener) {
listeners.push(listener);
return function () {
listeners = listeners.filter(item=>item!==listener);
}
}
dispatch({type:'@@INIT'});
return {
getState,
dispatch,
subscribe
}
}
bindActionCreator
虽然我们用dispatch派发事件,派发动作类型大概是一个有type属性的对象,我们将这个对象的方法封一个actions的文件夹,在dispach的时候直接调用action,列入将dispatch(()=>{type:add})
,此时我们需要建立dispatch和actions的关系
<button onClick={()=>bindActions.add1(1)}>+</button>
export default function (actions,dispatch) {
//actions = {add(){return {type:ADD}},minus(){return {type:'MINUS'}}}
return Object.keys(actions).reduce(
(memo,key) => {
memo[key]=(...args) => dispatch(actions[key](...args));
return memo;
},{});
}
combineReducers
返回一个函数,这个函数代表合并后的reducer,那么他一定有两个参数,最终返回新状态,我们迭代reducers每一个属性
//因为redux应用只能有一个仓库,只能有一个reducer
//把多个reducer函数合并成一个
export default function (reducers) {
//返回的这个函数就是合并后的reducer
return function (state={},action) {
let newState={};
for (let key in reducers) {
// key=counter1
//reducers[counter1]=counter那个reducer
//state合并后的状态树
//if (key===action.name) {
newState[key]=reducers[key](state[key],action);//state[key]老状态
//}
}
return newState;
}
}
我们可以发现,每个组件都需要获取仓库,管理订阅,向仓库派发事件,这样很融于,我们需要复用这样的逻辑,可以用高阶组件,或者将函数作为子组件,我们此时用这个库
applyMiddleware
中间件大概是redux里面比较难理解的部分啦
他的原理如上图,state和dispatch构成啦我们的仓库,我们可以向仓库发送action通过reducer改变状态,状态改变之后可以修改视图,用户可以通过鼠标点击视图,视图派发action,改变状态,形成循环,,有时候我们需要发布异步操作,想在派发前,派发后做一些额外动作,此时我们就需要插入中间件,我们的方法就是得到dispatch方法,重写dispacth,这里用到啦我们说的源码解析
一个redux中间件大概就是如下,之后可能会将redux和express和koa中间件做个对比给大家总结一下
function logger(store){//getState ,新的dispatch
return function(next){//store.dispatch旧的
return function action(){
console.log('old');
next()
console.log('new')
}
}
}
//解析过程
let logger = store => next =>action =>{
}
//以下是处理过程
function applyMiddleware(middleware){
return function(creacteStore){
return function(reducer){
let store = creacteStore(reducer);
let middleware2 = middleware(store)
let dispatch = middleware2(store.dispatch)
}
}
}
let store = applyMiddleware(logger)(creacteStore)(reducer)
import compose from './compose';
export default function (...middlewares) {
return function (createStore) {
return function (reducers) {
let store=createStore(reducers);//这就是原始的仓库 dispatch 就是原始的dispatch
let dispatch;//dispatch方法指向新的dispatch方法
let middlewares2 = middlewares.map(middleware=>middleware({
getState: store.getState,
dispatch:action=>dispatch(action)
}));//调用第一层去掉
dispatch=compose(...middlewares2)(store.dispatch);//再调用第二次把第二层去掉
return {
...store,
dispatch
}
}
}
}
compose整合中间件
function add1(str) {
return 1+str;
}
function add2(str) {
return 2+str;
}
function sum(a,b) {
return a+b;
}
//let ret=add1(add2(add3('zdl')));
//console.log(ret);
function compose1(...fns) {//[add1,add2,add3]
return function (...args) {
let last=fns.pop();
return fns.reduceRight((val,fn)=>fn(val),last(...args));
}
}
export default function(...fns) {
return fns.reduce((a,b)=>(...args)=>a(b(...args)));
}
/**
* 第一次的时候 a =add1 b=add3 let ret = add1(add2(...args))
* 第二次的时候 (...args)=>add1(add2(sum(...args)))
*/
let ret=compose(add1,add2,sum)('a','b');
console.log(ret);//123zdl
react-redux
有四个文件
- connect 将store和dispatch分别映射成props属性对象,返回组件
- context 上下文 导出Provider,,和 consumer
- Provider 一个接受store的组件,通过context api传递给所有子组件,优点类似于路由啦
- index
index
import Provider from './Provider';
import connect from './connect';
export {
Provider,
connect
}
Provider
/**
* 是一个组件,用来接受store,再经过它的手通过context api传递给所有的子组件
*/
import React,{Component} from 'react'
import {Provider as StoreProvider} from './context';
import PropTypes from 'prop-types';
export default class Provider extends Component{
//规定如果有人想使用这个组件,必须提供一个redux仓库属性
static propTypes={
store:PropTypes.object.isRequired
}
render() {
let value={store:this.props.store};
return (
<StoreProvider value={value}>
{this.props.children}
</StoreProvider>
)
}
}
context
import React from 'react'
let {Provider,Consumer}=React.createContext();
export {Provider,Consumer};
connect
这是个高阶函数,分别传入两个方法mapStateToProps,mapDispatchToProps,将store和dispatch分别映射成props属性对象,这样在页面就不需要饮用仓库,也不需要绑定action和dispatch,也不需要订阅状态,返回组件
import {Consumer} from './context';
import React,{Component} from 'react';
import {bindActionCreators} from '../redux';
/**
* connect实现的是仓库和组件的连接
* mapStateToProps 是一个函数 把状态映射为一个属性对象
* mapDispatchToProps 也是一个函数 把dispatch方法映射为一个属性对象
*/
export default function (mapStateToProps,mapDispatchToProps) {
return function (Com) {
//在这个组件里实现仓库和组件的连接
class Proxy extends Component{
state=mapStateToProps(this.props.store.getState())
componentDidMount() {
this.unsubscribe = this.props.store.subscribe(() => {
this.setState(mapStateToProps(this.props.store.getState()));
});
}
componentWillUnmount = () => {
this.unsubscribe();
}
render() {
let actions={};
//如果说mapDispatchToProps是一个函数,执行后得到属性对象
if (typeof mapDispatchToProps === 'function') {
actions = mapDispatchToProps(this.props.store.dispatch);
//如果说mapDispatchToProps是一个对象的话,我们需要手工绑定
} else {
actions=bindActionCreators(mapDispatchToProps,this.props.store.dispatch);
}
return <Com {...this.state} {...actions}/>
}
}
return () => (
<Consumer>
{
value => <Proxy store={value.store}/>
}
</Consumer>
);
}
}
更多专业前端知识,请上【猿2048】www.mk2048.com