Redux源码解析系列 (三)-- createStore

createStore源码地址为:https://github.com/reactjs/redux/blob/master/src/createStore.js

下面我来对其进行解析~

INIT

这个方法是redux保留用的,用来初始化reducer的状态

export const ActionTypes = {
  INIT: '@@redux/INIT'
}

前面说 createStore的作用就是:创建一个store来管理app的状态,唯一改变状态的方式就是dispatch一个action,最终返回一个object。

return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
}

不过replaceReducer,跟[$$observable]:都不常用~ ,所以这里只对前三的接口做解析。

createStore

在一个app里,只能有一个store,如果你想指明不同的state对应不同的action,可以用combineReducers去合并不同的reducer。

参数:

  • reducer(function):就是通过传入当前State,还有action,计算出下一个state,返回回来。
  • preloadedState(any):initial state
  • enhancer(function):增强store的功能,让它拥有第三方的功能,比如middleware.Redux里面唯一的enhancer就是applyMiddleware()
export default function createStore(reducer, preloadedState, enhancer) {
// 第一段说的就是当第二个参数没有传preloadedState,而直接传function的话,就会直接把这个function当成enhancer
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }
  // 当第三个参数传了但是不是function也会报错
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    //关键的一个就在于这里了,在前一篇讲applyMiddleware的时候介绍了这么做的意义,
    //实际就是把createStore这件事在applyMiddleware里面做,转移了锅。
    return enhancer(createStore)(reducer, preloadedState)
  }

  if (typeof reducer !== 'function') {
    throw new Error('Expected the reducer to be a function.')
  }

  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false
}

上面是第一个part,在校验完参数的正确之后,终于可以干点正事儿了。 createStore最终会返回一个Object,

{
 dispatch,
 subscribe,
 getState
}

接下来看看里面都做了什么:

getState

getState作用就是将当前state的状态返回回来,没啥好说的~

function getState() {
   return currentState
}

subscribe

作用:添加监听函数listener,它会在每次dispatch action的时候调用。

参数:listener(function): 在每一次dispatch action的时候都会调用的函数

返回:返回一个移除listener的函数

// 这个函数的作用就是,如果发现nextListeners,nextListeners指向同一个堆栈的话,就浅复制一份,这样改nextListeners就不会改到currentListeners
function ensureCanMutateNextListeners() {
    if (nextListeners === nextListeners) {
      nextListeners = currentListeners.slice()
    }
}

function subscribe(listener) {
    if (typeof listener !== 'function') {
      throw new Error('Expected listener to be a function.')
    }

    let isSubscribed = true

    ensureCanMutateNextListeners()
    // 直接将监听的函数放进nextListeners里
    nextListeners.push(listener)

    return function unsubscribe() {
    // 如果已经移除了就直接返回
      if (!isSubscribed) {
        return
      }

      isSubscribed = false

      ensureCanMutateNextListeners()
      // 没有移除的话,先找到位置,通过splice移除
      const index = nextListeners.indexOf(listener)
      nextListeners.splice(index, 1)
    }
  }

在使用的时候就可以:

const unsubscribe = store.subscribe(() =>
  console.log(store.getState())
)

unsubscribe()

dispatch

dispatch 作为一个重点函数~ 其实它的作用就是触发状态的改变。

参数:action(object),它是一个描述发生了什么的对象,其中type是必须的属性。

返回:这个传入的object

function dispatch(action) {
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
        'Use custom middleware for async actions.'
      )
    }
    //
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
        'Have you misspelled a constant?'
      )
    }
    // 防止多次dispatch请求同时改状态,一定是前面的dispatch结束之后,才dispatch下一个
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      isDispatching = true
      currentState = currentReducer(currentState, action)
    } finally {
      isDispatching = false
    }
    // 在dispatch的时候,又将nextListeners 赋值回currentListeners,
    const listeners = currentListeners = nextListeners
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }

    return action
  }

在上面一系列完成之后,需要初始化appState的状态。当INIT action被dispatched 的时候,每个reducer都会return回它的初始状态

dispatch({ type: ActionTypes.INIT })

参考资料: https://github.com/reactjs/redux/blob/master/src/createStore.js

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏青蛙要fly的专栏

项目需求讨论-Vlayout来快速构建及扩展复杂界面

大家好,今天又带来了项目中具体遇到的需求。做一个首界面,该首界面有很多功能块,同时这些功能块是动态的,因为登录的人的权限的不同,会显示不同的功能块,因为功能模块...

762
来自专栏菩提树下的杨过

silverlight如何在运行时用代码动态控制(或创建)动画

silverlight做一些复杂动画时,不可能所有的动画都事先用Blend之类的设计工具"画"好(或者在设计期就在vs里编好),很多时候我们希望在运行时能动态控...

17910
来自专栏Flutter入门到实战

一次使用Kotlin实现酷炫多选操作的尝试

“手机上的多选很难操作”,我们的设计师Vitaly Rubtsov如是说。大多数应用中的多选方案 -Telegram, Apple Music, Spotif...

682
来自专栏CNN

玩转AppBarLayout,更酷炫的顶部栏

上一篇文章《CoordinateLayout的使用如此简单 》对CoordinateLayout的使用做了讲解,今天我们再讲解常常与其一起使用的几个View:A...

872
来自专栏Android 开发者

[译] 论 Android 中 Span 的正确打开方式

1795
来自专栏岑玉海

hbase源码系列(十二)Get、Scan在服务端是如何处理?

继上一篇讲了Put和Delete之后,这一篇我们讲Get和Scan, 因为我发现这两个操作几乎是一样的过程,就像之前的Put和Delete一样,上一篇我本来只打...

43410
来自专栏禁心尽力

反射+自定义注解---实现Excel数据列属性和JavaBean属性的自动映射

简单粗暴,直奔主题。 需求:通过自定义注解和反射技术,将Excel文件中的数据自动映射到pojo类中,最终返回一个List<pojo>集合?   今天我只是通...

2159
来自专栏flutter开发者

[Flutter Widget]Tooltip

在前面的文章中我们讲到了Wrap的用法,介绍了Flutter中的流式布局,在文章的最后让大家实现如下效果:

1005
来自专栏walterlv - 吕毅的博客

CaptureMouse/CaptureStylus 可能会失败

2017-10-09 11:05

431
来自专栏刘望舒

LeakCanary看这一篇文章就够了

LeakCanary是Square公司基于MAT开源的一个内存泄漏检测工具,在发生内存泄漏的时候LeakCanary会自动显示泄漏信息。

1155

扫码关注云+社区