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 条评论
登录 后参与评论

相关文章

来自专栏GIS讲堂

geotools等值线生成

前文中,提到了等值面的生成,后面有人经常会问等值线的生成,本文在前文的基础上做了一点修改,完成了等值线的geotools生成。

1285
来自专栏Danny的专栏

数据脱敏——基于Java自定义注解实现日志字段脱敏

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

1461
来自专栏我杨某人的青春满是悔恨

设计模式之结构型模式(上)

GoF 归纳整理的23个设计模式依据其目的可以分为创建型(Creational)、结构型(Structural)和行为型(Behavioral)三种。关于创建型...

793
来自专栏海纳周报

详解Python的is操作符

is 操作符是Python语言的一个内建的操作符。它的作用在于比较两个变量是否指向了同一个对象。 与 == 的区别 class A(): def __i...

4209
来自专栏企鹅FM

深入浅出Kotlin协程

协程(Coroutines)在Kotlin1.x版本还处于实验阶段,android平台可以使用如下方式引入:

9136
来自专栏MasiMaro 的技术博文

OLEDB 静态绑定和数据转化接口

OLEDB 提供了静态绑定和动态绑定两种方式,相比动态绑定来说,静态绑定在使用上更加简单,而在灵活性上不如动态绑定,动态绑定在前面已经介绍过了,本文主要介绍OL...

771
来自专栏高爽的专栏

Java线程(篇外篇):阻塞队列BlockingQueue

       好久没有写文章了,这段时间事情比较杂,工作也比较杂乱,上周日刚搬完家,从自建房搬到了楼房,提升了一层生活品质,哈哈!不过昨天晚上在公交车上钱包被偷...

2070
来自专栏王硕

原 pg查询树的简单解读

34313
来自专栏chenssy

【追光者系列】HikariCP源码分析之字节码修改类库Javassist委托实现动态代理

很多人都会问HikariCP为什么那么快?之前的两篇文章【追光者系列】HikariCP源码分析之FastList 和 【追光者系列】HikariCP源码分析之C...

1972
来自专栏编程

C语言嵌入式系统编程修炼之内存操作

这是我13年前创作和发表在互联网上的文章,这么多年过去了,这篇文章仍然在到处传播。现在贴回Linuxer公众号。 全文目录: C语言嵌入式系统编程修炼之道——背...

1975

扫码关注云+社区