Redux源码之createStore

前言

本文会按照 createStore 的源码,由上至下分别从参数、内部定义的变量、getState、subScribe订阅、dispatch派发、replaceReducer 替换 reducer 几个方面进行介绍。关于combineReducers applyMiddleware 的源码在 上一篇 有进行说明。

参数处理

createStore(reducer, preloadedState, enhancer)

createStore 接收三个参数:reducer、初始化的 state、中间件(加强dispatch)

// reducer 必须是 function
if (typeof reducer !== 'function') {
    throw new Error(/*...*/)
}
// 第二、三个参数不能同时为 function
if (
    (typeof preloadedState === 'function' && typeof enhancer === 'function') ||
    (typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
    throw new Error(/*...*/)
}
// 如果只传了两个参数,并且第二个参数是 function,那么默认第二个参数就是 enhancer
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
}
// 如果有第三个参数并且是个 function 就执行这个 function,这里是对中间件的处理
if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
        throw new Error(/*...*/)
    }
    return enhancer(createStore)(
        reducer,
        preloadedState,
    )
}

通过上面的代码可以看到,第二个参数 preloadedState 不是必须传的,那当不传第二个参数,state 是如何初始化的呢?

dispatch({ type: ActionTypes.INIT });

在 createStore 底部执行了一次 type 为随机的派发,这时执行 reducer,返回结果就会赋给 currentState 完成初始化

变量

let currentReducer = reducer
let currentState = preloadedState
let currentListeners = new Map()
let nextListeners = currentListeners
let listenerIdCounter = 0
let isDispatching = false

currentReducer 就是我们传入 createStore 的 reducer
currentState 就是初始化的 state
currentListeners 是一个 map用来存放订阅事件
nextListeners 是 currentListeners 的副本
isDispatching 用来判断是否处于 dispatch

getState

function getState() {
    if (isDispatching) {
      throw new Error(
        'You may not call store.getState() while the reducer is executing. ' +
          'The reducer has already received the state as an argument. ' +
          'Pass it down from the top reducer instead of reading it from the store.'
      )
    }
    return currentState;
  }

getState 在不是 dispatch 的状态下,执行返回当前的 state。

subscribe

function subscribe(listener: () => void) {
    // listener 必须是一个 function
    if (typeof listener !== 'function') {
        throw new Error(
            `Expected the listener to be a function. Instead, received: '${kindOf(
                listener
            )}'`
        )
    }
    // 在 dispatch 时不可以订阅
    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 加入新的 listener
    const listenerId = listenerIdCounter++
    nextListeners.set(listenerId, listener)
    // 退订
    return function unsubscribe() {
        // 如果没订阅,直接返回
        if (!isSubscribed) {
            return
        }
        // dispatch 中不能退订
        if (isDispatching) {
            throw new Error(
                'You may not unsubscribe from a store listener while the reducer is executing. ' +
                'See https://redux.js.org/api/store#subscribelistener for more details.'
            )
        }

        isSubscribed = false

        ensureCanMutateNextListeners()
        // 将退订的方法从新的监听队列移除
        nextListeners.delete(listenerId)
        // 清空当前监听队列
        currentListeners = null
    }
}

ensureCanMutateNextListeners

function ensureCanMutateNextListeners() {
    // 判断新监听队列和当前队列是否指向同一地址
    if (nextListeners === currentListeners) {
        // 浅拷贝一份 currentListeners。currentListeners 和 nextListeners 不再指向同一地址 后面会在 nextListeners 里面进行新的监听事件添加。
        nextListeners = new Map()
        currentListeners.forEach((listener, key) => {
            nextListeners.set(key, listener)
        })
    }
}

为什么采用两个监听队列?

currentListeners 代表的是当前的监听队列,nextListeners代表的是下次要执行的监听队列,那么为什么需要两个监听队列呢? 我们设想一个场景,如果我们在订阅时进行嵌套,如下

subscribe(() => {
    console.log("第一次订阅")
    subscribe(() => {
        console.log("嵌套订阅")
    })
});

这里在 subscribe 订阅时嵌套 再次订阅,嵌套订阅并不会立即执行,而是会被保存在 nextListeners 在下次执行 这样可以保证在订阅过程中任何新订阅和取消订阅不会对当前的监听产生影响。

dispatch

function dispatch(action) {
    // 首先判断 action 是否是一个纯对象
    if (!isPlainObject(action)) {
      throw new Error(
        `Actions must be plain objects. Instead, the actual type was: '${kindOf(
          action
        )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
      )
    }
    // 判断 action 是否存在 type 属性
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
      )
    }
    // 判断 action 的 type 是否是一个对象
    if (typeof action.type !== 'string') {
      throw new Error(
        `Action "type" property must be a string. Instead, the actual type was: '${kindOf(
          action.type
        )}'. Value was: '${action.type}' (stringified)`
      )
    }
    // 如果正处于 dispatch 状态,不可以再进行 dispatch 会抛出错误
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }

    try {
      // 到这里就可以开始进行 dispatch 了,首先把状态设置为 正在 dispatch
      isDispatching = true
      // 执行 reducer,传入 旧的state 和 action,将执行结果也就是新的 state 赋值给 currentState
      currentState = currentReducer(currentState, action)
    } finally {
      // 无论是否成功,最后都要关闭 dispatch 状态
      isDispatching = false
    }
    // 将 nextListeners 赋值给 currentListeners,此时他们指向同一地址
    const listeners = (currentListeners = nextListeners)
    // 遍历执行所有的监听函数
    listeners.forEach(listener => {
      listener()
    })
    return action
  }

isPlainObject

export default function isPlainObject(obj): boolean {
  // action 必须是个非空对象
  if (typeof obj !== 'object' || obj === null) return false
  
  let proto = obj
  // 获取到 obj 原型链非 null 的顶层,赋值给 proto
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }
  // 判断 obj 的原型是否为 proto action。其实就是判断 obj 是否存在继承关系,存在则返回 false
  return Object.getPrototypeOf(obj) === proto
}

replaceReducer

  function replaceReducer(nextReducer) {
    // 新的 reducer 必须是一个 function
    if (typeof nextReducer !== 'function') {
      throw new Error(
        `Expected the nextReducer to be a function. Instead, received: '${kindOf(
          nextReducer
        )}`
      )
    }
    // 进行替换
    currentReducer = nextReducer
    // 进行一次派发
    dispatch({ type: ActionTypes.REPLACE })
  }

replaceReducer 就是简单的进行替换然后进行一次派发

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UmijS 是一个基于 React 的前端开发框架,它提供了一种简单易用的方式来构建单页应用程序。如果你想在 UmiJS 中使用 Redux,可以按照以下步骤进行操作: 1. 安装 Redux 和 React-Redux:在你的项目目录下运行以下命令来安装所需的依赖包: ``` npm install redux react-redux --save ``` 2. 创建 Redux store:在你的项目中创建一个 `store.js` 文件,并使用 Redux 的 `createStore` 方法来创建一个 Redux store。例如: ```javascript import { createStore } from 'redux'; import rootReducer from './reducers'; // 导入你的根reducer const store = createStore(rootReducer); export default store; ``` 3. 创建 reducers:在你的项目中创建一个 `reducers.js` 文件,用于定义你的 reducers。例如: ```javascript // 定义初始状态 const initialState = { // 初始状态数据 }; // 定义 reducer const rootReducer = (state = initialState, action) => { switch (action.type) { // 处理不同的 action 类型,更新 state default: return state; } }; export default rootReducer; ``` 4. 使用 Redux Provider:在你的根组件中使用 Redux 的 `Provider` 组件,将 Redux store 传递给 UmiJS 应用程序。例如: ```javascript import { Provider } from 'react-redux'; import store from './store'; export function rootContainer(container) { return React.createElement(Provider, { store: store }, container); } ``` 5. 在组件中使用 Redux:使用 `react-redux` 提供的 `connect` 方法来连接你的组件到 Redux store,并将需要的 state 和 action 传递给组件。例如: ```javascript import { connect } from 'react-redux'; function MyComponent(props) { // 使用 props 中的 state 和 action // ... } const mapStateToProps = state => { // 将需要的 state 映射到组件的 props return { // ... }; }; const mapDispatchToProps = dispatch => { // 将需要的 action 映射

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值