内容简介:在没有redux出来之前,父组件和子组件之间,平行组件之间传递和修改状态,需要将状态和修改状态的方法逐级往下传,组件嵌套过深则很容易出现管理混乱的问题。所以redux就是为了解决状态管理而诞生的。为了理解上面的东西,看下面的用例:在创建组件的时候,组件应该是存粹的,使用store.dispatch等其它变量,需要根据情况而区别的引入不同的变量,最好使用state或者props,所以需要将action和dispatch抽离封装然后赋予到组件的prop上
在没有redux出来之前,父组件和子组件之间,平行组件之间传递和修改状态,需要将状态和修改状态的方法逐级往下传,组件嵌套过深则很容易出现管理混乱的问题。所以redux就是为了解决状态管理而诞生的。
Redux实现
基础框架
//用来创建Store function createStore(reducer){} // 抽离出不同的type调用dispatch函数的复用部分 function bindActionCreators(actions,dispatch){} // 合并reducer function combineReducers (reducers){} // 注册中间件 function applyMiddleware (...middlewares){} //??????? function compose(...args){} export { createStore, bindActionCreators, combineReducers, compose, applyMiddleware } 复制代码
Redux的核心createStore
- 创建一个state变量用来存储平行组件间需要共享的变量
- 提供getState方法给外界获取state
- 提供subscribe方法给外界订阅回调在dispatch修改state时执行
- 提供dispatch方法给外界调用用户注册的reucer来修改state并执行订阅的回调
function createStore(reducer){ let state; //存放状态 let listeners = []; //存放订阅的回调 function dispath(action){ state = reducer(state, action); // 调用reducer返回新的state listeners.forEach(fn=>fn()); // 发布所有订阅的回调 } // 派发初始动作,type为reducer中没有的类型,目的是初始化状态为用户设置的状态 dispatch({type:'@INIT'}); function getState(){ // 暴露的state属性不希望别人能改,改了也不会影响原有的状态 return JSON.parse(JSON.stringify(state)); } function subscribe(fn){ //订阅回调,并返回一个从listeners删除回调的函数 listeners.push(fn); return ()=>{listeners = listeners.filter(l=>l!=fn)}; } } return { getState, dispatch, subscribe } } 复制代码
为了理解上面的东西,看下面的用例:
let initState = { title: { color: "red", text: "kbz" } }; function reducer(state = initState, action) { switch (action.type) { case "CHANGE_TITLE_COLOR": return { ...state, title: { ...state.title, color: action.color } }; break; case "CHANGE_TITLE_TEXT": return { ...state, content: { ...state.title, text: action.text } }; break; } return state; } let store = createStore(reducer); let unsubcribe = store.subscribe(function() { console.log(store.getState().title.color); }); setTimeout(() => { store.dispatch({ type: "CHANGE_TITLE_COLOR", color: "yellow" }); }, 1000); setTimeout(() => { unsubcribe(); store.dispatch({ type: "CHANGE_TITLE_COLOR", color: "blue" }); }, 2000); 复制代码
bindActionCreators
在创建组件的时候,组件应该是存粹的,使用store.dispatch等其它变量,需要根据情况而区别的引入不同的变量,最好使用state或者props,所以需要将action和dispatch抽离封装然后赋予到组件的prop上
- 抽离type
//action-types.js export const ADD = 'ADD'; export const MINUS = 'MINUS'; 复制代码
- 抽离action
//actions.js import * as types from '../action-types'; let actions = { add(count){ return {type:types.ADD,count} }, minus(count){ return {type:types.MINUS,count} } } export default actions 复制代码
- 批量抽离dispatch
function bindActionCreators(actions,dispatch){ let obj = {} for(let key in actions){ obj[key] = (...args)=>dispatch(actions[key](...args)) } return obj; } 复制代码
- 属性赋值
import React,{Component} from 'react'; import {render} from 'react-dom'; import Counter from './components/Counter'; import {bindActionCreators} from 'redux'; import store from './store'; inmport action form './action' let props= bindActionCreators(actions,store.dispatch) render(<Counter {...props}></Counter>,window.root); 复制代码
combineReducers
为了更好的模块化管理,可以将每个组件的reducer分开来创建,然后再通过combineReducers将所有的reducer合并起来,其原理就是创建一个函数,dispatch的时候将所有reducer都执行一次
function combineReducers (reducers){ //返回一个总的totalReducer,和所有的reducer一样接收state和action return (state={},action)=>{ // totalState登记每一个组件的state // 遍历执行所有的reducer,将返回的state重新登记在totalState中 let obj = {}; for(let key in reducers){ obj[key] = reducers[key](state[key],action) } return obj; } } 复制代码
applyMiddleware
常用的中间件
中间件原理:在原来的dispatch方法外包装一层函数,扩展其他功能,又能保证原来功能的使用。
// 打印日志中间件 let reduxLogger = (store)=>(dispatch)=>(action)=>{ console.log('prev',store.getState()); dispatch(action) console.log('next',store.getState()); } // let reduxThunk = (store)=>(dispatch)=>(action)=>{ // 如果是函数将正真的dispatch传给用户,用户抉择是否要派发 if(typeof action === 'function'){ return action(dispatch,store.getState); } dispatch(action); // 直接把对象派发即可 } let reduxPromise = (store)=>(dispatch)=>(action)=>{ // 判断当前action是不是一个promise,如果是promise就执行,执行的时候只会管成功的结果 if( action.then &&typeof(action.then) == 'function'){ return action.then(dispatch); }else if(action.payload && action.payload.then){ //action.payload是否为promise return action.payload.then(data=>{ dispatch({...action,payload:data}); },err=>{ dispatch({...action,payload:err}); return Promise.reject(err); // 对外抛出错误 }) } return dispatch(action); } 复制代码
applyMiddleware基础原理
let applyMiddleware = (middleware)=> (createStore)=> (reducer)=>{ let store = createStore(reducer); // 返回新的dispatchL:(action)=>{xxxxxx} let fn = middleware(store); let newDispatch = fn(store.dispatch); //覆盖原有的dispatch,返回{getState,dispatch:newDispatch,subscribe} return {...store,dispatch:newDispatch}; } // 典型的柯里化,把多个middleware连起来,后面compose会介绍 export default applyMiddleware(reduxLogger)(createStore)(reducer); 复制代码
compose
compose的原理
项目中使用的插件不止一个,在使用多个插件的情况下,需要使用一个方法将多个插件合并成一个。
function add(a,b){ return a+b; } function toUpperCase(str){ return str.toUpperCase(); } function len(str){ return str.length } function compose(...args){ return args.reduce((a,b)=>{(...args)=>a(b(...args))}); } compose(len,toUpperCase,add)(a,b); //(a,b) => len(toUpperCase(add(a,b))) 复制代码
a | b | 返回函数 |
---|---|---|
len | toUpperCase | (...args)=>len(toUpperCase(...args)) |
(...args)=>{len(toUpperCase(...args)} | add | (...args)=>len(toUpperCase(add(...args))) |
完善applyMiddleware
let reduxLogger = (store)=>(dispatch)=>(action)=>{ console.log('prev',store.getState()); dispatch(action) console.log('next',store.getState()); } let applyMiddleware = (...middlewares)=> (createStore)=> (reducer)=>{ let store = createStore(reducer); let fns = middlewares.map(middleware=>{ return middleware(store) //返回的函数接受disopatch用于在原来的基础上扩展 }); // compose(fn1,fn2)(store.dispatch) //fn执行返回一个新的包装dispatch函数传给fn1 let newDispatch = compose(...fns)(store.dispatch); return {...store,dispatch:newDispatch}; //将合并后的dispatch覆盖原来的最初的dispatch } function compose(...args){ return args.reduce((a,b)=>((...args)=>a(b(...args)))); } 复制代码
redux完整代码
function createStore(reducer,fn) { let state; let listeners = []; let dispatch = (action) => { state = reducer(state,action); listeners.forEach(fn=>fn()); } dispatch({type:'@INIT'}); // createStore(reducer,applyMiddleware(...middlewares))一步到位 // 在内部使用applyMiddleware(...middlewares)(createStore)(reducer) if(typeof fn === 'function'){ return fn(createStore)(reducer); } let getState = ()=> JSON.parse(JSON.stringify(state)); let subscribe = (fn)=>{ listeners.push(fn); return ()=>{ listeners = listeners.filter(l=>l!=fn); } } return {getState,subscribe,dispatch} } function bindActionCreators(actions,dispatch){ let obj = {} for(let key in actions){ obj[key] = (...args)=>dispatch(actions[key](...args)) } return obj; } let combineReducers = (reducers)=>{ return (state={},action)=>{ let obj = {} for(let key in reducers){ obj[key] = reducers[key](state[key],action) } return obj; } } let applyMiddleware = (...middlewares)=> (createStore)=> (reducer)=>{ let store = createStore(reducer); let fns = middlewares.map(middleware=>{ return middleware(store) }); let newDispatch = compose(...fns)(store.dispatch); return {...store,dispatch:newDispatch}; } function compose(...args){ return args.reduce((a,b)=>((...args)=>a(b(...args)))); } export { createStore, bindActionCreators, combineReducers, compose, applyMiddleware } 复制代码
React-redux
Redux和React的关系
Redux是一款状态管理库,并且提供了react-redux库来与React亲密配合,这两者的关系如下图:
从上面可以看出,React-redux通过Provider和connet将Redux和React联系起来:
React-redux框架
import React,{Component} from 'react'; import {bindActionCreators} from './redux' let Context = React.createContext(); //将store挂载在contex上,供嵌套组件使用 class Provider extends Component{} // connect的作用就是获取store,子组件获取contex上的store let connect = (mapStateToProps,mapDispatchToProp)=>{} export { Provider, connect } 复制代码
Provider
React会提供一个createContext的API,调用它会生成一个Context,里面包含了Provider组件和Consume组件,Provider提供一个状态供跨组件使用,需要使用状态的组件只要嵌套在Consume中就获取Provider提供的状态。 让react用起来更得心应手——(react基础解析) 里面有介绍,这里不赘述。
let Context = React.createContext(); class Provider extends Component{ // 将React-redux中的Provide包装了react提供的API生成的Context.Provider //<Provider store={xxxx}></Provider>,将store挂载在contex上 render(){ return <Context.Provider value={{store:this.props.store}}> {this.props.children} //子组件 </Context.Provider> } } 复制代码
connect
既然有挂载store,就必然有子组件获取store,connect的作用就是获取提供好的store
//调用方法:connect(mapStateToProps,mapDispatchToProp)(Com) // connect是一个高阶组件,调用后的返回一个组件 let connect = (mapStateToProps,mapDispatchToProp)=>(Com) =>{ return ()=>{ // 高阶组件的特点就是把组件中公用的逻辑抽取来,返回一个经过处理的组件 class Proxy extends Component{ state = mapStateToProps(this.props.store.getState()) componentWillMount(){ this.unsub = this.props.store.subscribe(()=>{ this.setState(mapStateToProps(this.props.store.getState())) }) } componentWillUmount(){ this.unsub() } //mapStateToProps就是将state中的部分或全部状态映射到需要的组件中作为其props //mapDispatchToProp就是将action中已经绑定成dispatch形式的action按需求映射到需要的组件作为其props render(){ let b if(typeof mapDispatchToProp === 'function'){ b = mapDispatchToProp(this.props.store.dispatch); }else{ // bindActionCreators把直接将所有action的绑定成diapatch(action)形式组成一个对象 b = bindActionCreators(mapDispatchToProp,this.props.store.dispatch) } //将所有的state和修改state的方法以props的方式传入 return <Com {...this.state} {...b}></Com> } } //调用Consumer将获取到的store传给包装Com的Proxy return <Context.Consumer> {({store})=>{ return <Proxy store={store}></Proxy> }} </Context.Consumer> } } 复制代码
用例:
import React,{Component} from 'react'; import actions from '../store/actions/counter'; import {connect} from 'react-redux'; class Counter extends Component{ render(){ return (<div> <button onClick={()=>{ this.props.add(2); }}>+</button> {this.props.number} <button onClick={()=>{ this.props.minus(2); }}>-</button> </div>) } } // mapStateToProps用户自己定义需要的状态 let mapStateToProps = (state)=>{ return {number:state.counter.number} } // action也是用户自己定义的,可以是函数可以是对象 // 如果传递过来的不是方法是对象,会把这个对象自动用bindActionCreators包装好 export default connect(mapStateToProps,actions)(Counter); 复制代码
结语:
个人使用一种框架时总有一种想知道为啥这样用的强迫症,不然用框架用的不舒服,不要求从源码上知道其原理,但是必须得从心理上说服自己。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Kotlin实战
【美】Dmitry Jemerov(德米特里·詹莫瑞福)、【美】 Svetlana Isakova(斯维特拉娜·伊凡诺沃) / 覃宇、罗丽、李思阳、蒋扬海 / 电子工业出版社 / 2017-8 / 89.00
《Kotlin 实战》将从语言的基本特性开始,逐渐覆盖其更多的高级特性,尤其注重讲解如何将 Koltin 集成到已有 Java 工程实践及其背后的原理。本书分为两个部分。第一部分讲解如何开始使用 Kotlin 现有的库和API,包括基本语法、扩展函数和扩展属性、数据类和伴生对象、lambda 表达式,以及数据类型系统(着重讲解了可空性和集合的概念)。第二部分教你如何使用 Kotlin 构建自己的 ......一起来看看 《Kotlin实战》 这本书的介绍吧!