Redux是一个状态管理库,一般用于大型应用中。它出现的原因是,应用越来越复杂, 通过状态提升已经不能满足应用的需求。
1. Redux设计思想
1. 将整个应用的状态state(一个状态树)存在一个仓库中,唯一一个store中。
2. 组件通过store的dispatch方法,派发动作action到store中。
3.store通过唯一的一个根reducer,根据原始state和action,产生新的状态。
4. 其他组件可以订阅store中状态值的变化,用于刷新组件
2. createStore
该方法用于创建仓库
const store = createStore(reducer[, initState]);
1. 参数
reducer是处理器函数。initState是仓库的初始状态。
1) 在创建store时赋初值,本质上相当于给reducer函数中的传入state参数为initState。
2)initState省略时, 还可以通过reducer函数的state参数赋初值。
redux会在创建仓库的时候调用一次dispatch,触发自身定义的@@redux/INIT动作。然后获取之后的state值。
2. dispatch
dispath函数接受一个含有type属性的纯对象进行派发动作,最后返回动作本身。
function dispatch(action) { // 提示必须是纯对象 if(!isPlainObject(action)) { throw new Error('必须是纯对象'); } // 提示不能是undefined if (typeof action === 'undefined') { throw new Error('type不能是undefined') } currentState = currentReducer(currentState, action); for(let i=0; i < listeners.length; i++) { listeners[i](); } // 注意dispatch返回的是action return action; } /****调用后返回action本身****/ const result = store.dispatch({type: types.INCREASE }); console.log(result); //{type: "INCREASE" }
3. subscribe/unsubscribe
// 监听函数render1订阅store的状态变化 const unscribe = store.subscribe(render1); // 运行返回值,取消订阅 unscribe();
订阅函数本身只是将监听函数加入队列,监听函数的执行在dispatch方法调用时。
function subscribe(listenr) { listeners.push(listenr); return function() { // 取消订阅实际是将监听函数移除监听队列 const index = listeners.indexOf(listenr); listeners.splice(index, 1); } }
开发中我们要实时刷新页面,需要依靠渲染函数取订阅(subscribe)store的变化。
componentWillUnmount() { this.unsubcribe(); } componentDidMount() { this.unsubcribe = store.subscribe(() => { this.setState({ number: store.getState() }) }) }
4. store.getState()
获取仓库当前的状态。第一次获取时,有两个地方可以赋初值。
3. bindActionCreators
将动作生成函数绑定dispatch方法。从而可以直接调用actionCreator方法。
function bindActionCreator(action, dispath) { return function() { return dispath(action.apply(this, arguments)) } } export default function(actions, dispath) { if(typeof actions === 'function') { return bindActionCreator(actions, dispath); } const bondActionCreators = {}; for(let key in actions) { bondActionCreators[key] = bindActionCreator(actions[key], dispath) } return bondActionCreators; }
使用时:
import actions from '../store/actions'; import store from '../store'; import {bindActionCreators} from '../redux'; const bondActions = bindActionCreators(actions, store.dispatch); <button onClick={bondActions.increase}>+</button> <button onClick={bondActions.decrease}>-</button>
4. combineReducers
当应用中模块过多,使用一个reducer函数很难处理时,可以写多个reducer,然后通过该方法合并成一个根reducer。
本质上,是遍历所有的reducer处理状态树,然后返回新的状态。
export default function(reducers) { // 返回一个根reducer,且该reducer返回最新的state树 return function(state={}, action) { const nextState = {}; const previousState = state; // 获取各个子reducer的key值 const reducerKeys = Object.keys(reducers); // 遍历reduers for(let i=0; i< reducerKeys.length; i++) { const key = reducerKeys[i]; nextState[key] = reducers[key](previousState[key], action) } return nextState; } }
5. applyMiddleware
用于向仓库应用中间件。而中间是用于拦截并改写dispatch方法。有两种用法:
1) 级联调用,这样的好处是每个方法中传参可以不固定
let store = applyMiddleware(logger, xx, xx)(createStore)(reducer, initState);
2)中间的参数可以省略
let store = createStore(reducer, initState, applyMiddleware(logger, xxx))
原理
从最后一个中间件开始执行,返回的结果(处理过的dispatch)作为倒数第二个的dispatch参数,依此类推。
中间件
中间件的通用写法是:
let middleware = store => dispatch => action => { // TODO }