redux源码学习笔记 - createStore
本篇是学习redux源码的一些记录,学习的redux版本是^4.0.1。
在页面开发时,需要管理很多状态(state),比如服务器响应,缓存数据,UI状态等等···当页面的庞大时,状态就会变的混乱。redux就派上用场了,它最大的特点就是使状态变化变的可预测。
redux提供一个管理state的仓库(store),并且规定了store只能通过reducer(函数)来更新,而reducer必须通过dispatch(action)来触发,action就是普通的JavaScript对象,它约定了执行的类型并且传递数据。使得state的变化是可以预测的,同样的步骤会得到同样的state。
从第一步创建仓库开始看起 createStore(reducer, preloadedState, enhancer)。
开始已经提到redux是管理一个store,那么第一步就是创建store,一般最简单的就是以下形式:
let store = createStore(reducer,preloadedState,enhancer);
- reducer是更新store的函数,必传参数,function类型
- preloadedState为初始状态,为可选参数。如果reducer是使用 combineReducers 合并多个函数而成的,要注意preloadedState的数据格式要和 combineReducers 中的keys一致。
- enhancer在学习applyMiddleware时一起说明。
看一下createStore 源码中的关键部分:
// 记录reducer函数、初始状态、监听函数
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
// 创建store时,触发一个空的action,这样如果没有初始状态,就会返回reducer中的默认状态
dispatch({ type: ActionTypes.INIT })
// 返回一个提供了多种方法的对象
return {
dispatch, //触发action的方法
subscribe, //增加监听方法
getState, //获取当前状态的方法
replaceReducer, //更换reducer方法
[$$observable]: observable
}
这里是用了闭包,在createStore的作用域中创建了currentState 变量来记录状态,currentReducer来记录reducer函数,currentListeners来记录所有的监听函数。然后返回一个对象,对象中的方法可以获取currentState、触发reducer来更新currentState,添加监听函数,替换reducer等。
这个对象就是 store , 而state,reducer,listeners保存在createStore的作用域中,只有通过store中的方法可以访问到。
getState()
只有store.getState()能获取到仓库的state --> currentState变量
function getState() {
if (isDispatching) { //··· }
return currentState
}
dispatch()
只有store.dispatch(action)可以触发更新state。注意在redux中action必须是一个纯对象,而且必须有type字段指定动作类型,dispatch中有对与这些的校验。
function dispatch(action) {
/*-- action必须是对象 --*/
if (!isPlainObject(action)) { //··· }
/*-- action必须有type字段--*/
if (typeof action.type === 'undefined') { //··· }
if (isDispatching) { //··· }
/*-- 触发reducer,更新state --*/
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
/*-- 执行监听函数 --*/
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
从这一段currentState = currentReducer(currentState, action)看出currentState是通过执行reducer函数更新的。
而且也知道了reducer函数的参数情况:
- 当前状态
- action对象,且有type字段
如果想在state变化时做点什么,就需要用到subscribe方法添加监听函数
subscribe(listener)
只看关键代码,其实就是维护了一个保存监听函数的数组。从上面dispatch的代码listener()可以看出,这些函数会在dispatch(action)的时候触发。
而且每次新增listener的时候都会返回一个取消监听的方法unsubscribe,可以在适当的时候取消监听。
function subscribe(listener) {
/*-- 增加监听 --*/
nextListeners.push(listener)
/*-- 返回一个取消监听的函数 --*/
return function unsubscribe() {
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
replaceReducer(nextReducer)
可以更改reducer函数,很简单,重新赋值。
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') { //··· }
// 更新reducer函数
currentReducer = nextReducer
dispatch({ type: ActionTypes.REPLACE })
}
实例
上面对createStore的实现有了大致的了解,再看看栗子
action中需要type字段来标记要执行的字段,会定义为字符串常量;一般会使用单独的模块来管理。
/*-- 动作类型 actionTypes.js --*/
export const ADD_TODO = 'ADD_LIST';
export const COMPLETE_TODO = 'COMPLETE_TODO';
action 必须是一个纯JavaScript对象。可以通过创建action的函数返回,这样就方便传递数据。
/*-- action对象 action.js --*/
import { ADD_TODO, COMPLETE_TODO} from "./actionTypes";
export function addList (text) {
// 返回action对象
return {
type: ADD_TODO,
text
}
}
export function completeList (id,bl = true) {
return {
type: COMPLETE_TODO,
id,
bl
}
}
reducer 是一个纯函数,接受旧的state和action,返回新的state。只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
/*-- reducer函数 todolist.js --*/
import {ADD_TODO, COMPLETE_TODO} from '../actionTypes';
let id = 0;
export default function todoList(state = [],action) {
// 根据type区分如何更新state
switch (action.type) {
case ADD_TODO :
// 不要直接修改state,要返回一个新的state
return [...state,{
id: id++,
text: action.text,
complete:false
}]
case COMPLETE_TODO :
return state.map( item => {
if (item.id === action.id){
return Object.assign({},item,{complete:action.bl})
}
return item;
})
default:
return state;
}
}
使用 createStore 创建 store
/*-- 创建store --*/
import {createStore} from 'redux';
import reducer from './reducer/todolist';
let store = createStore(reducer);
更新 state
import {addList,completeList} from './store/actions';
store.dispatch(addList('测试数据'));
addList('测试数据')返回的是 action {type: ADD_TODO,text:'测试数据'}。
在todoList函数中,执行完后返回新的state [{id:0,text:'测试数据',complete:false}]。
获取 state
console.log(store.getState()); // [{id:0,text:'测试数据',complete:false}]
添加/删除 监听函数
// 添加
unsubscribe = store.subscribe(function(){ // 每次更新state都会执行此函数
console.log(store.getState());
});
// 取消
unsubscribe()
redux源码学习笔记 - createStore的更多相关文章
- Redux源码学习笔记
https://github.com/reduxjs/redux 版本 4.0.0 先了解一下redux是怎么用的,此处摘抄自阮一峰老师的<Redux 入门教程> // Web 应用是一个 ...
- redux源码学习笔记 - applyMiddleware
在创建store时,createStore(reducer, preloadedState, enhancer),除了reducer函数,初始状态,还可以传入enhancer.这个enhancer在c ...
- redux源码学习笔记 - combineReducers
上一篇有了解到,reducer函数的两个为:当前state和此次dispatch的action. state的结构是JavaScript对象,每个key都可以代表着不同意义的数据.比如说 { list ...
- Redux源码分析之createStore
接着前面的,我们继续,打开createStore.js, 直接看最后, createStore返回的就是一个带着5个方法的对象. return { dispatch, subscribe, getSt ...
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...
- Underscore.js 源码学习笔记(上)
版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}()); 这样的东西,我们应该知道这是一个 IIFE(立即执行 ...
- redux源码图解:createStore 和 applyMiddleware
在研究 redux-saga时,发现自己对 redux middleware 不是太了解,因此,便决定先深入解读一下 redux 源码.跟大多数人一样,发现 redux源码 真的很精简,目录结构如下: ...
- AXI_LITE源码学习笔记
AXI_LITE源码学习笔记 1. axi_awready信号的产生 准备接收写地址信号 // Implement axi_awready generation // axi_awready is a ...
- Hadoop源码学习笔记(6)——从ls命令一路解剖
Hadoop源码学习笔记(6) ——从ls命令一路解剖 Hadoop几个模块的程序我们大致有了点了解,现在我们得细看一下这个程序是如何处理命令的. 我们就从原头开始,然后一步步追查. 我们先选中ls命 ...
随机推荐
- vue项目中跳转到外部链接方法
当我们在文件中,如果是vue页面中的内部跳转,可以用this.$router.push()实现,但是如果我们还用这种方法跳到外部链接,就会报错,我们一看链接的路径,原来是我们的外部链接前面加上了htt ...
- java -关于包装类自动装箱与拆箱拓展+整形常量池
关于自动装箱与拆箱 1.包装类与基本数据类型的自动转换,叫装箱和拆箱(类型自动转换) 2.自动装箱拆箱是在编译器,编译器自动配转换方法,实现装箱和拆箱.所以这个过程发生在编译期 3.只有需要相互类型转 ...
- Vmware Workstation - linux系统下 VmTools 安装
程序版本 : VMware® Workstation 14 Pro 系统环境 : win10 64位下 ubuntu-14.04.5-desktop-amd64 问题:在运行linux系统过程中,de ...
- C# WPF 通过委托实现多窗口间的传值
在使用WPF开发的时候就不免会遇到需要两个窗口间进行传值操作,当然多窗口间传值的方法有很多种,本文介绍的是使用委托实现多窗口间的传值. 在上代码之前呢,先简单介绍一下什么是C#中的委托(如果只想了解如 ...
- JS獲取URL的參數
function GetQueryString(name) { var reg = new RegExp("(^|&)"+ name +"=([^&]*) ...
- Maven学习 六 pom.xml文件
java jar包的搜索网址:http://mvnrepository.com/ pom作为项目对象模型.通过xml表示maven项目,使用pom.xml来实现.主要描述了项目:包括配置文件:开发者需 ...
- springboot pom.xml记
本文包括: springboot 基本pom.xml配置 热部署 配置打包插件 maven pom.xml配置详解 1. springboot 基本pom.xml配置 <project xmln ...
- centos7.5搭建cdh5.13.0
序言 本文集群搭建为三台机器,cdh版本为5.13.0,以下是安装过程中所用到的软件包等,可以自行下载.一.前期准备1.安装环境 系统:centos7.5/最小安装版本/64位 内存:主节点 --&g ...
- java -version显示版本和JAvA_HOME配置不一样
当你需要安装多个版本的jdk时,可能会遇到更改了JAVA_HOME后java -version不变的情况. 一般情况下,将你的JAVA_HOME改为你要用的jdk的安装路径,然后你使用的就是这个版本的 ...
- 2019.03.09 codeforces620E. New Year Tree(线段树+状态压缩)
传送门 题意:给一棵带颜色的树,可以给子树染色或者问子树里有几种不同的颜色,颜色值不超过606060. 思路:颜色值很小,因此状压一个区间里的颜色用线段树取并集即可. 代码: #include< ...