Redux的作用是什么

Redux的作用在于实现状态传递状态治理。在这里你可能会说了,如果是状态传递,那我props的传递不也是能够达到这样的成果吗?context上下文计划不也是能够达到这样的成果吗?没错,是这样的,然而上述的两种计划是有局限性的。

  • props计划只实用于父子组件传递状态。
  • context上下文计划尽管可能在根组件上定义上下文,然而有两种缺点

    • 只有上下文外面的状态产生扭转,就会从新渲染整个组件树,进而会产生宏大的性能开销。
    • 组件的逻辑与状态的耦合度太高,不利于解耦,也就是无奈实现对状态的对立治理。

既然Redux的作用是对状态的治理与传递,那么他的作用场景呢?当然了你能够依据下面说的两种计划对Redux的应用做取舍,Redux的实质就是全局变量被协调治理。

  • 如果波及多个状态,且多个状态会被多个组件应用到,比方商城购物车场景,你就能够毫不犹豫的思考用Redux
  • 如果波及多个状态,然而状态虽多然而是用的组件惟一,或者有关联关系的组件应用,你就大可不必应用Redux,如果状态不是那么多,那就更不用应用Redux了。

除此之外,Redux还有一个长处就是,不仅仅是React自身可能应用,就连别的框架,比方jQuerykerry_domvue等都能够应用,然而比照于vue来讲的话,vue有本人比拟好的的状态治理库vuex,好了废话不多说了,咱们先来看看Redux在我的项目中是如何是用的。

Redux的应用

// store.jsimport { createStore } from "redux";import reducer from "./reducer";export default createStore(reducer);// reducer.jsimport {cloneDeep} from 'lodash';const initilaValue = {    count: 0};const reducer = (state = initilaValue, action) => {    state = cloneDeep(state)    const { type, payload } = action;    switch (type) {      case "add":        state.count += payload;        break;      case "reduce":        state.count -= payload;        break      default:    }  return state;};export default reducer;// App.jsimport React, {Component} from 'react';import store from "./store";export default class App extends Component {    componentDidMount() {      //reducer不会触发页面变动,须要state来触发      store.subscribe(() =>{        this.setState(store.getState())      })    }    render() {    //获取reducer数据    const {count} = store.getState()      return (        <div>          <div type='primary' onClick={this.reduce}>-</div>          <span>{count}</span>          <div type='primary' onClick={this.add}>+</div>        </div>      );    }    reduce = () => {      //告诉reducer页面数据变动了      store.dispatch({        type: 'reduce',        payload: 1      })    }    add = () => {      //告诉reducer页面数据变动了      store.dispatch({        type: 'add',        payload: 1      })    }}

上述代码就能够实现count的加减计算了,咱们能够看到有几处须要留神的中央。

  • store.js文件外面的createStore
  • reducer.js文件外面的cloneDeepreturn statestate = initialValue
  • App.js文件外面的dispatchgetStatetypepayload

很显著createStore的作用就是创立仓库,getState为获得以后的state值,dispatch为某个操作之后派发给store去更新某个statetype为具体的某种交互,payload为每次交互的具体内容。各位同学能够看失去我在reducer中做了一次state的深克隆,这是为什么呢?是因为在每一次的action中咱们拿到的是同一个state内存地址,咱们的冀望是不论你在switch中如何更改state然而我不心愿在这一步就扭转了公共状态中的count,只有在我return的时候才会去更改真正的公共状态,也就是说reducer函数执行产生的公有闭包外面的公共状态信息。而state = initialValue这一步的操作就是第一次派发的时候,reducer接管的state为空,咱们把根底值赋给它。理解了这么多,咱们还是去看一下他的源码是如何实现的吧。

Redux的源码

//Redux/redux/src/index.tsexport {  createStore, // 创立仓库api  combineReducers, // 合并Reducer  bindActionCreators, // 转化action  applyMiddleware, // 中间件利用计划  compose,// 函数组合  __DO_NOT_USE__ActionTypes}

下面的index.ts文件夹外面暴露出了几个api,咱们次要针对createStore看看。

export default function createStore(  reducer: Reducer<S, A>,  preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,  enhancer?: StoreEnhancer<Ext, StateExt>): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {  // createStore函数不反对第二三四参数为函数  // 详见https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers  if (    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||    (typeof enhancer === 'function' && typeof arguments[3] === 'function')  ) {    throw new Error(      ...    )  }  // 第二参数是函数,且第三参数不传  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {    enhancer = preloadedState as StoreEnhancer<Ext, StateExt>    preloadedState = undefined  }  // 有第三参数且不是函数  if (typeof enhancer !== 'undefined') {    if (typeof enhancer !== 'function') {      throw new Error(        `Expected the enhancer to be a function. Instead, received: '${kindOf(          enhancer        )}'`      )    }    return enhancer(createStore)(      reducer,      preloadedState as PreloadedState<S>    ) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext  }  // 如果reducer不是函数,要报错  if (typeof reducer !== 'function') {    throw new Error(      `Expected the root reducer to be a function. Instead, received: '${kindOf(        reducer      )}'`    )  }  let currentReducer = reducer // 以后的reducer  let currentState = preloadedState as S // 以后的state  let currentListeners: (() => void)[] | null = [] // 事件池  let nextListeners = currentListeners  let isDispatching = false // 正在派发  /**   * This makes a shallow copy of currentListeners so we can use   * nextListeners as a temporary list while dispatching.   *   * This prevents any bugs around consumers calling   * subscribe/unsubscribe in the middle of a dispatch.   */   function ensureCanMutateNextListeners() {    if (nextListeners === currentListeners) {      nextListeners = currentListeners.slice()    }  }  // 返回以后的state  function getState(): S {    if (isDispatching) {      throw new Error(        ...      )    }    return currentState as S  }  //向事件池中增加更新事件  function subscribe(listener: () => void) {    // 校验是不是函数    if (typeof listener !== 'function') {      throw new Error(       ...      )    }    if (isDispatching) {      throw new Error(        'You may not call store.subscribe() while the reducer is executing. ' +          'If you would like to be notified after the store has been updated, subscribe from a ' +          'component and invoke store.getState() in the callback to access the latest state. ' +          'See https://redux.js.org/api/store#subscribelistener for more details.'      )    }    let isSubscribed = true    //防止反复增加    ensureCanMutateNextListeners()    nextListeners.push(listener)    //subscribe函数每执行一次,都会返回一个unsubscribe用来从事件池中移除以后事件    return function unsubscribe() {      if (!isSubscribed) {        return      }      if (isDispatching) {        throw new Error(          ...        )      }      isSubscribed = false      ensureCanMutateNextListeners()      // 移除以后事件      const index = nextListeners.indexOf(listener)      nextListeners.splice(index, 1)      currentListeners = null    }  }  // 派发函数  function dispatch(action: A) {    // 如果传过来的action不是对象报错    if (!isPlainObject(action)) {      throw new Error(       ...      )    }    //每一个action中都须要一个type字段,没有就报错     if (typeof action.type === 'undefined') {      throw new Error(       ...      )    }    //正在派发中..    if (isDispatching) {      throw new Error('Reducers may not dispatch actions.')    }    try {      isDispatching = true      // 执行reducer,扭转state      currentState = currentReducer(currentState, action)    } finally {      isDispatching = false    }    // dispatch告诉事件池去执行事件,遍历执行    const listeners = (currentListeners = nextListeners)    for (let i = 0; i < listeners.length; i++) {      const listener = listeners[i]      listener()    }    return action  }  // 替换replaceReducer  function replaceReducer<NewState, NewActions extends A>(    nextReducer: Reducer<NewState, NewActions>  ): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext {    // 入参不是函数,报错    if (typeof nextReducer !== 'function') {      throw new Error(       ...      )    }    ...    // 刚开始进来要派发一次,同步state,其中{ type: ActionTypes.REPLACE }为惟一标识    // 假使不为惟一标识的话,那可能一开始就毁坏了状态value    dispatch({ type: ActionTypes.REPLACE } as A)    // change the type of the store by casting it to the new store    return store as unknown as Store<      ExtendState<NewState, StateExt>,      NewActions,      StateExt,      Ext    > &      Ext  }  ...  // 初始化store的时候,须要派发一次同步state  dispatch({ type: ActionTypes.INIT } as A)  const store = {    dispatch: dispatch as Dispatch<A>,    subscribe,    getState,    replaceReducer,    [$$observable]: observable  } as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext  // 返回仓库  const store = createStore({count:0})  return store}

参考React实战视频解说:进入学习

的确短短几百行代码实现了redux,为此咱们也来实现一个简易版的redux示意敬意,咱们的redux只实现getStatedispatchcreateStore办法。

// myReduximport {cloneDeep} from 'lodash'export function createStore(reducer) {  if(typeof reducer !== 'function') {    throw new Error('reducer must be an function')  }  let state,      listeners = [];  const getState = () => {    // 深克隆一个state    return cloneDeep(state);  }  const subscribe  = (listener) => {    if(typeof listener !== 'function'){      throw new Error('listener must be an function')    }    //去重    if(!listeners.includes(listener)){      listeners.push(listener)    }    // 源码外面执行subscribe,返回一个卸载函数    return function unsubscribe(){      listeners.filter(action => action!== listener)    }  }  const dispatch = (action) => {    if(typeof action !== 'object' && action !== null){      throw new Error('action must be a object')    }    // 判断有没有type    if(typeof action.type === undefined){      throw new Error('action.type must existence')    }    //执行     try {      state = reducer(state, action)    } catch (error) {      //...    }    // 告诉事件池中的办法执行    listeners.forEach(listener=>{      if(typeof listener === 'function'){        listener();      }    })  }  //第一次进来要派发一次,同步初始状态  dispatch({    type:typeof Symbol !== undefined ? Symbol('ABC') : '惟一值'  })  // 暴露出办法  return {    getState,    dispatch,    subscribe  }}

各位能够去codeSandBox下面去实际一下,当然了这只是简易版的redux,官网举荐应用react-redux来进行理论的我的项目开发,因为他只关注于数据管理。

总结

redux的大抵工作流如此: