前端学习记录
  • 前言及目录
  • 前端基础
    • HTML
    • CSS
      • CSS学习之布局
    • JavaScript
      • 跟着月影学JavaScript
      • JavaScript之对象、原型链及继承
      • JavaScript中的类
      • onclick与addEventListener区别
      • JS手撕题
    • HTTP与浏览器
      • HTTP实用指南
      • Web开发的安全之旅
    • 通用知识
      • 前端必须知道的开发调试知识
      • 前端设计模式应用
      • Web 标准与前端开发
  • 数据结构及算法
    • 数据结构
      • 1、线性表(List)
      • 2、堆栈(Stack)
      • 3、队列(Queue)
      • 4、二叉树(Binary Tree)
      • 5、二叉搜索树与平衡二叉树(BST & AVL)
      • 6、堆(Stack)& 哈夫曼树 & 并查集
      • 7、图(Graph)
        • 图论——解决最小生成树问题(Kruskal算法&Prim算法)
      • 8、排序(sort)
      • 9、散列表(hash)
      • 数据结构习题
        • 第一周:最大子列和算法、二分查找
        • 第二周:线性结构
        • 第三周:栽树(二叉树等)
        • 第四周:二叉搜索树&二叉平衡树
        • 第五周:堆&哈夫曼树&并查集
        • 第六周:图(上)连通集 、DFS&BFS
        • 第七周:图(中)Floyd算法求最短路
        • 第八周:图(下)
        • 第九周:排序(上)归并&堆排序
        • 第十周:排序(下)
        • 第十一周:散列查找 & KMP
    • CS基础
      • 编译原理 实验一 词法分析器设计
      • 编译原理 实验二 LL(1)分析法程序
    • LeetCode
      • 冲刺春招-精选笔面试 66 题大通关
        • day1:21. 合并两个有序链表、146. LRU 缓存、25. K 个一组翻转链表
        • day2:14. 最长公共前缀、3. 无重复字符的最长子串、124. 二叉树中的最大路径和
        • day3:206. 反转链表、199. 二叉树的右视图、bytedance-016最短移动距离
        • day4:1. 两数之和、15. 三数之和、42. 接雨水
        • day5:7. 整数反转、215. 数组中的第K个最大元素、23. 合并K个升序链表
        • day6:33. 搜索旋转排序数组、54. 螺旋矩阵、bytedance-006. 夏季特惠
        • day7:53. 最大子数组和、152. 乘积最大子数组、41. 缺失的第一个正数
        • day8:20. 有效的括号、200. 岛屿数量、76. 最小覆盖子串
        • day9:105. 从前序与中序遍历序列构造二叉树、103. 二叉树的锯齿形层序遍历、bytedance-010. 数组组成最大数
        • day10:94. 二叉树的中序遍历、102. 二叉树的层序遍历、394. 字符串解码
        • day11:415. 字符串相加、5. 最长回文子串、72. 编辑距离
        • day12:64. 最小路径和、300. 最长递增子序列、bytedance-004. 机器人跳跃问题
        • day13:88. 合并两个有序数组、31. 下一个排列、4. 寻找两个正序数组的中位数
        • day14:121. 买卖股票的最佳时机、56. 合并区间、135. 分发糖果
        • day15:232. 用栈实现队列、22. 括号生成、128. 最长连续序列
        • day16:bytedance-007. 化学公式解析、129. 求根节点到叶节点数字之和、239. 滑动窗口最大值
        • day17:141. 环形链表、236. 二叉树的最近公共祖先、92. 反转链表 II
        • day18:322. 零钱兑换、198. 打家劫舍、 bytedance-003. 古生物血缘远近判定
        • day19:160. 相交链表、143. 重排链表、142. 环形链表 II
        • day20:704. 二分查找、43. 字符串相乘、bytedance-002. 发下午茶
        • day21题目:69. x 的平方根、912. 排序数组、887. 鸡蛋掉落
        • day22:151. 颠倒字符串中的单词、46. 全排列、2. 两数相加
      • 剑指 Offer
        • 剑指offer day1 栈与队列(简单)
        • 剑指offer day2 链表(简单)
        • 剑指offer day3 字符串(简单)
        • 剑指offer day4 查找算法(简单)
        • 剑指offer day5 查找算法(中等)
        • 剑指offer day6 搜索与回溯算法(简单)
        • 剑指offer day7 搜索与回溯算法(简单)
        • 剑指offer day8 动态规划(简单)
        • 剑指offer day9 动态规划(中等)
        • 剑指offer day10 动态规划(中等)
        • 剑指offer day11 双指针(简单)
        • 剑指offer day12 双指针(简单)
        • 剑指offer day13 双指针(简单)
        • 剑指offer day14 搜索与回溯算法(中等)
        • 剑指offer day15 搜索与回溯算法(中等)
        • 剑指offer day16 排序(简单)
        • 剑指offer day17 排序(中等)
      • 剑指 Offer 专项突击版
  • 前端进阶
    • React
      • 响应式系统与 React
      • React学习小记
      • Redux学习之Redux三原则、createSore原理及实现
    • Vue
    • TypeScript
      • TypeScript入门
      • TypeScript 类型体操练习
        • Easy题(13/13)
        • Middle(20/72)
    • 前端工程化
      • Webpack知识体系
    • Node
    • 前端动画与绘图
      • WebGL基础
      • 前端动画简介
      • Floating UI 使用经验分享 - Popover
      • Floating UI 使用经验分享 - Dialog
      • Three.js 学习
        • 学习记录
        • 资源记录
    • 前端性能优化
    • 跨端
      • RN 学习小记之使用 Expo 创建项目
    • 开源
    • SEO 优化
      • 搜索引擎优化 (SEO) 新手指南笔记
  • 笔面试记录
    • 面经集锦
      • 2022春暑期实习MetaApp一二面面经
      • 2022春暑期实习字节前端一面凉经
    • 笔试复盘
      • 2022春暑期实习-美团前端-笔试
      • 2022春暑期实习-360前端-笔试(AK)
      • 2022春暑期实习-京东前端-笔试
      • 2022春暑期实习-网易雷火前端-笔试(AK)
      • 2022春暑期实习-网易互联网前端-暑期实习笔试
由 GitBook 提供支持
在本页
  • 简介
  • 什么需要使用 Redux
  • Redux 三原则
  • 原则1:单一不可变状态树
  • 原则2:状态树只读
  • 原则3:要描述状态变化,必须编写一个纯函数(Reducer)
  • 编写一个带有测试的计数器Reducer
  • 存储方法:getState()、dispatch()和subscribe()
  • getState() 获取状态
  • dispatch() 调度,发起action
  • subscribe() 注册
  • 实现一个简易版的createStore~!
  • 改进计数器
  • 编写Counter组件
  • 调用createStore、添加监听
  • 查看过程
  • 总结

这有帮助吗?

在GitHub上编辑
导出为 PDF
  1. 前端进阶
  2. React

Redux学习之Redux三原则、createSore原理及实现

上一页React学习小记下一页Vue

最后更新于3年前

这有帮助吗?

首先要明确一点,虽然 Redux 是一个很不错的管理状态工具,但还是要考虑下它是否适合你的场景。

不要仅仅因为有人说过应该使用 Redux 而使用,而是应该花一些时间来了解使用它的潜在好处和取舍。

开新坑……记录一下学习使用Redux的历程,主要来自、及其里面的教程里的。

这篇文章是下面这个教学视频里的1-8期视频,阐述了Redux三原则和Redux中的Reducer、(getState、dispatch、subscribe)以及createStore的原理及实现,并且实现了一个简易的计数器。看完基本上对Redux就有了一个大致的了解

为什么学捏,主要是因为最近看的项目或多或少都有Redux的使用,不学压根看不懂

Redux 入门 —— 系列视频 Redux 的创建者 Dan Abramov 在 30 个短片(2-5 分钟)中展示了各种概念。链接的 Github 仓库包含视频的笔记和转录。

简介

什么是Redux

Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理。

可以让你开发出行为稳定可预测的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供超爽的开发体验,比如有一个时间旅行调试器可以编辑后实时预览。

Redux 除了和 React 一起用外,还支持其它界面库。它体小精悍(只有2kB,包括依赖),却有很强大的插件扩展生态。

什么需要使用 Redux

首先要明确一点,虽然 Redux 是一个很不错的管理状态工具,但还是要考虑下它是否适合你的场景。不要仅仅因为有人说过应该使用 Redux 而使用 - 应该花一些时间来了解使用它的潜在好处和取舍。

当在自己的项目中遇到如下问题时,建议开始使用 Redux:

  • 你有很多数据随时间而变化

  • 你希望状态有一个唯一确定的来源(single source of truth)

  • 你发现将所有状态放在顶层组件中管理已不可维护

Redux 三原则

原则1:单一不可变状态树

单一不可变状态树(The Single Immutable State Tree),注意几个关键词:单一、不可变、状态树。

Redux的第一个原则就是:应用程序的整个状态将由一个JavaScript对象来表示,无论这个应用是简单的小应用还是拥有大量UI和状态变化的复杂应用程序。而这个JavaScript对象就称之为状态树。

这是一个Todo应用程序中可能具备的状态:

"current_state:"
[object Object] {
  todos: [[object Object] {
    completed: true,
    id: 0,
    text: "hey",
  }, [object Object] {
    completed: false,
    id: 1,
    text: "ho",
  }],
  visibilityFilter: "SHOW_ACTIVE"
}

原则2:状态树只读

Redux 的第二个原则是状态树是只读的。我们是没有办法直接修改或写入它的,只能通过**“发起Action”**这个行为来对其进行修改。

Action是描述更改的一个普通JS对象,它是对该数据所做的更改的最小表示形式,它的结构完全取决于我们自己,唯一的要求是他必须有一个绑定的属性type,(通常使用字符串,因为它是可序列化的)。

dispatch an action

例如:Todo应用程序中,显示Todo的组建并不知道如何将项目添加到列表中,他们知道的只是他们需要分派(dispatch)一个action,它具有一个类型type:"add todo",以及一个todo的文本和一个序号。

如果切换一个任务,同样,组件不知道它是怎么发生的。他们所需要做的就是分派一个具有type的action,切换todo,并传入想要切换到的todo的ID。

如上可以看出,状态是只读的,并且只能通过dispatch操作进行修改。

原则3:要描述状态变化,必须编写一个纯函数(Reducer)

而Redux的第三个原则就是:要描述状态变化,必须编写一个纯函数,该纯函数采用应用的先前状态(previous state)和发起的action(the action being dispatched),然后返回应用的下一个状态(next state )。而这个纯函数称为Reducer。

理解纯函数与非纯函数

首先我们要理解什么是纯函数/非纯函数,因为Redux有时候需要我们编写纯函数。

纯函数是指一个函数的返回结果只依赖于它的参数,并且在执行过程中没有任何副作用,这也意味着,它不会对外界产生任何影响。

可以非常确定的说,使用相同的参数集调用纯函数,将得到相同的返回值。他们是可预测的。

此外,纯函数不会修改传递给它们的值。例如,接受数组的squareAll函数不会覆盖此数组内的项。相反,它通过使用项目映射返回一个新的数组。

纯函数示例:

function square(x) {
    return x*x;
}
function squareAll(items) {
    return items.map(square);	// 注意,这里是生成了一个新的数组而非直接return items
}

非纯函数示例:

function square(x) {
  updateXInDatabase(x);	// 对数据库中的x也产生了影响
  return x * x;
}
function squareAll(items) {
  for (let i = 0; i < items.length; i++) {
    items[i] = square(items[i]);	// 并且直接修改了items...
  }
}

Reducer

React 开创了这样一种观点:当 UI 层被描述为应用程序状态的纯函数时,它是最可预测的。

Redux 用另一个想法补充了这种方法:应用中的状态突变必须由纯函数描述,该函数采用上一个状态和正在调度的操作,并返回应用程序的下一个状态。

即使在大型应用程序中,仍然只需要一个函数来计算应用程序的新状态。它根据整个应用程序的先前状态和正在调度的操作来执行此操作。

但是,这个操作不一定很慢。如果状态的某些部分没有更改,则其引用可以保持原样。这就是使Redux快速的原因。

初始状态

初始state中,todos为空,过滤器为显示全部

添加Todo

变化如图: 一开始的state中,todos没有内容,过滤器为显示全部。发起action之后的state中todos多了个todo,过滤视图未变化

完成Todo

更改过滤视图

再添加一个todo后点击过滤器Active,观察前后state,可以发现,只是visibilityFilter状态由"SHOW_ALL"改变为"SHOW_ACTIVE"了,todos的内容还是没有变化的(abcd并没有被删掉)

编写一个带有测试的计数器Reducer

const counter = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

expect(
  counter(0, { type: 'INCREMENT' })
).toEqual(1);

expect(
  counter(1, { type: 'INCREMENT' })
).toEqual(2);

expect(
  counter(2, { type: 'DECREMENT' })
).toEqual(1);

expect(
  counter(1, { type: 'DECREMENT' })
).toEqual(0);

expect(
  counter(1, { type: 'SOMETHING_ELSE' }) 
).toEqual(1);

expect(
  counter(undefined, {})
).toEqual(0);

如上,counter这个Reducer设置了两个可识别的type(INCREMENT、DECREMENT),分别表示计数+1,-1。在写入Reducer时,如果传入的 state 是未定义的,则需要返回一个表示初始状态的对象(initstate)。在这个计数器的例子中,我们返回0,因为我们的计数从 0 开始。如果传入的 action 不是Reducer所能识别的(SOMETHING_ELSE),我们只返回当前state。

存储方法:getState()、dispatch()和subscribe()

const counter = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

const { createStore } = Redux; // Redux CDN import syntax
// import { createStore } from 'redux' // npm module syntax

const store = createStore(counter);

参数

返回值

createStore所创建的store有3个重要方法

getState() 获取状态

dispatch() 调度,发起action

subscribe() 注册

如果需要解绑这个变化监听器,执行 subscribe 返回的函数即可。

// ... `counter` reducer as above ...

const { createStore } = Redux;
const store = createStore(counter);

store.subscribe(() => {
  document.body.innerText = store.getState();
});

document.addEventListener('click', () => {
    store.dispatch({ type : 'INCREMENT' })
});

按照以上方法的话,初始状态不会被更新,因为渲染发生在subscribe的回调之后,而经过补救过后:

const render = () => {
  document.body.innerText = store.getState();
};

store.subscribe(render);
render(); // 先调用一次来渲染初始状态0, 之后的渲染则在每次dispatch之后

document.addEventListener('click', () => {
    store.dispatch({ type : 'INCREMENT' })
});

上面的官网摘下来的文档看似很难理解,看看接下来的简易实现就能理解了。

实现一个简易版的createStore~!

在前面的学习中,我们研究了如何使用createStore(),但是为了更好理解它,让我们从头开始编写它!

首先复盘前面的案例,我们知道

  • createStore函数接收一个reducer函数,这个reducer函数返回当前state,会被内部的dispatch函数所调用

  • createStore函数创建出来的store需要拥有两个变量:

    • state 当前状态,它是一个JavaScript对象,

    • listeners 监听器数组,它是一个函数数组对象

  • createStore函数创建出来的store需要拥有这三个方法:getState,dispatch和subscribe

    • getState 方法返回当前state

    • dispatch 函数是更改内部state的唯一方法,它传入一个action,通过将内部的当前state和action传入reducer函数(createStore的入参)来计算新的state。更新后,我们通知每个变化监听器(通过调用它们) dispatch

    • 在返回store时,我们需要要填充初始状态。我们要分派一个假的 action 来让 reducer 返回初始值。

const createStore = (reducer) => {	// 返回 store,可以调用getState、dispatch、subscribe
	let state;	
    let listeners = [];
    const getState = () => state;   // 外部可以通过调用getState获取当前state

    const dispatch = (action) => {
        state = reducer(state, action); // 因为reducer是纯函数,所以每次返回的state不会修改原先的state~
        listeners.forEach(listener => listener());     // 在dispatch事件中,调用reducer成功后调用监听器们~
    }

    const subscribe = (listener) => {   // 添加监听!
        listeners.push(listener);
        return () => {      // 为了能够取消监听器,返回一个函数
            listeners = listeners.filter(l => l !== listener);
        }
    }

    dispatch({});   // 为了让state有一个初始状态!

    return { getState, dispatch, subscribe };
};

改进计数器

在上文我们实现了一个计数器的Reducer,在这里我们改进一下,用React实际的将其渲染出来

编写Counter组件

编写一个Counter组件,该组件是一个"哑"组件(dump component)。它不包含任何业务逻辑。

哑组件仅指定如何将当前状态呈现到输出中,以及如何将通过 prop 传递的回调函数绑定到事件处理程序。

// Counter 组件
const Counter = ({
  value,
  onIncrement,
  onDecrement,
  onElse
}) => (
  <div>
    <h1>{value}</h1>
    <button onClick={onIncrement}>+</button>
    <button onClick={onDecrement}>-</button>
    <button onClick={onElse}>else</button>
  </div>
);
// 该组件的渲染函数
const render = () => {
  console.log("render!");
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => store.dispatch({
        type:'INCREMENT'
      })}
      onDecrement={() => store.dispatch({
        type:'DECREMENT'
      })}
      onElse={() => store.dispatch({
        type:'else'
      })}
      />, document.getElementById('root')
  );
}

当该哑组件呈现时,我们指定其值应该从Redux存储的当前状态中获取值。当用户按下一个按钮时,相应的action将被dispatch到Redux的store。

调用createStore、添加监听

调用createStore创建一个store, 调用store.subscribe添加一个render监听器

const counter = (state = 0, action) => {
  console.log("now state:", state);
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
} 
// store create with counter reducer
const { createStore } = Redux;
const store = createStore(counter);
store.subscribe(render);  // 添加监听,每次状态更新都会重新渲染
render();

reducer指定如何根据当前state和传入的action计算下一个state。 最后,我们订阅store(传入render函数),这样render函数在每次调用dispatch改变state时运行。

ps:原文这里是 "Finally, we subscribe to the Redux store so our render() function runs any time the state changes so our Counter gets the current state." 说是每当state变化时触发render,根据上文createStore的实现来说感觉不准确,state就算值没变化也会触发,每次dispatch即发送action时都会render,实际试了试也是这样的(也许可以理解为,Redux要求每次dispatch都有改变状态的意图,后面视频要是有提到的话我再回来勘误)

查看过程

通过下面的例子了解一下这个过程:

初始值

一开始什么都不做,可以看到createStore的时候就进行了一次dispatch,通过reducer(即counter函数)将state置为初始值0后进行了一次渲染。(注意)

增加计数

点一下+,发现又调用了一次render

else

总结

首先要明确一点,虽然 Redux 是一个很不错的管理状态工具,但还是要考虑下它是否适合你的场景。

  • 不要仅仅因为有人说过应该使用 Redux 而使用,而是应该花一些时间来了解使用它的潜在好处和取舍。

看完这1-8期视频,基本上了解了Redux什么时候用比较好与它的缺点,了解了Redux三原则和Redux中的Reducer、(getState、dispatch、subscribe)以及createStore的原理及实现,并且实现了一个极其简易的计数器(顺带知道了哑组件是啥)。

Redux三原则:

  • 应用程序的整个状态将由一个JavaScript对象来表示,而这个JavaScript对象就称之为状态树。

  • 状态树是只读的。没办法直接修改或写入,只能通过**“发起Action”**这个行为来间接对其进行修改。

  • 描述状态变化必须编写一个纯函数,采用应用的先前状态state和发起的action,然后返回应用的下一个state(next state )。而这个纯函数称为Reducer。

createStore函数接收一个reducer函数,这个Reducer函数返回当前state,会被内部的dispatch函数所调用

  • createStore函数创建出来的store需要拥有两个变量:

    • state 当前状态,它是一个JavaScript对象,

    • listeners 监听器数组,它是一个函数数组对象

  • createStore函数创建出来的store还需要拥有这三个方法:getState,dispatch 和subscribe

    • getState 方法返回当前state

    • dispatch 函数传入一个action,通过将内部的当前state和action传入reducer函数(createStore的入参)来计算新的state。更新后,我们通知每个变化监听器(通过调用它们) dispatch

    • 在返回store时,我们需要要填充初始状态。我们要分派一个假的 action 来让 reducer 返回初始值。

在之前的博客里也提到过:,这里再重温一遍。

之前在学习React的时候就了解过了,不可变这个概念,对于很多场景下都有帮助,如React官方文档井字棋实现中的 功能,倘若每一步都在原来的对象上进行变换,撤销等工作就会变得非常复杂。

下面是

请添加图片描述
请添加图片描述

点击一个todo将其置为完成,可以看到发起这个action的时候,todos的文本没有变化,状态complete被置为完成了……

请添加图片描述

我们要编写的第一个函数是针对计数器示例的减速器。我们还将使用 来进行断言。

本节使用了Redux中内置的函数。我们使用ES6析构语法引入了.

创建一个 Redux 来以存放应用中所有的 state。应用中应有且仅有一个 store。

reducer (Function): 接收两个参数,分别是当前的 state 树和要处理的 ,返回新的 。

[preloadedState] (any): 初始时的 state。 在同构应用中,你可以决定是否把服务端传来的 state 水合(hydrate)后传给它,或者从之前保存的用户会话中恢复一个传给它。如果你使用 创建 reducer,它必须是一个普通对象,与传入的 keys 保持同样的结构。否则,你可以自由传入任何 reducer 可理解的内容。

enhancer (Function): Store enhancer,可选。可以用第三方第能力如中间价、时间旅行、持久化来增强 store。是一个组合 store creator 的高阶函数,返回一个新的强化过的 store creator。Redux 中唯一内置的 store enhander 是 。

(): 保存了应用所有 state 的对象。改变 state 的惟一方法是 action。你也可以 state 的变化,然后更新 UI。

检索 Redux 存储的当前状态。返回应用当前的 state 树。它与 store 的最后一个 reducer 返回值相同。

是最常用的。分发 action。这是触发 state 变化的惟一途径。

它将使用当前 getState()的结果和传入的 action 以同步方式调用 store 的 reduce 函数。它的返回值会被作为下一个 state。从现在开始,这就成为了 的返回值,同时变化监听器(change listener)会被触发。

添加一个变化监听器。每当 dispatch action 的时候就会执行,state 树中的一部分可能已经变化。可以在回调函数里调用 来拿到当前 state。

你可以在变化监听器里面进行 ,但你需要注意下面的事项:

监听器调用 仅仅应当发生在响应用户的 actions 或者特殊的条件限制下(比如: 在 store 有一个特殊的字段时 dispatch action)。虽然没有任何条件去调用 在技术上是可行的,但是随着每次 改变 store 可能会导致陷入无穷的循环。

订阅器(subscriptions) 在每次 调用之前都会保存一份快照。当你在正在调用监听器(listener)的时候订阅(subscribe)或者去掉订阅(unsubscribe),对当前的 不会有任何影响。但是对于下一次的 ,无论嵌套与否,都会使用订阅列表里最近的一次快照。

订阅器不应该注意到所有 state 的变化,在订阅器被调用之前,往往由于嵌套的 导致 state 发生多次的改变。保证所有的监听器都注册在 启动之前,这样,在调用监听器的时候就会传入监听器所存在时间里最新的一次 state。

这是一个底层 API。多数情况下,你不会直接使用它,会使用一些 React(或其它库)的绑定。如果你想让回调函数执行的时候使用当前的 state,你可以 。 Store 也是一个 , 所以你可以使用 的这样的库来 subscribe 订阅更新。

subscribe 传入一个listener函数作为参数,将其放入内部的listener数组,为了取消订阅事件监听器,subscribe需要返回一个函数, 调用这个返回的函数就可以取消监听,这个函数内部通过将listeners数组赋值为一个新的监听器数组(去除了与当前监听相同引用后返回的新监听器数组)。subscribe

改进后示例:

运行试试:

请添加图片描述
请添加图片描述

点了好几次else之后,发现每次都会重新渲染,但是state的值看起来似乎没变,而且组件表面上看也没有变化

subscribe 传入一个listener函数作为参数,将其放入内部的listener数组,为了取消订阅事件监听器,subscribe需要返回一个函数, 调用这个返回的函数就可以取消监听,这个函数内部通过将listeners数组赋值为一个新的监听器数组(去除了与当前监听相同引用后返回的新监听器数组)。subscribe

Redux 中文官网
Redux入门系列视频
笔记和转录
Redux入门系列视频
笔记和转录
Redux: The Single Immutable State Tree from @dan_abramov on @eggheadio
Redux: Describing State Changes with Actions from @dan_abramov on @eggheadio
Redux: Pure and Impure Functions | egghead.io
Redux: The Reducer Function | egghead.io
【第二届青训营-寒假前端场】- 「跟着月影学 JavaScript」笔记
时间旅行
一个完整示例
Redux: Writing a Counter Reducer with Tests | egghead.io
expect
eggheadio-projects/getting-started-with-redux: null - Plunker (plnkr.co)
Redux: Store Methods: getState(), dispatch(), and subscribe() | egghead.io
createStore
createStore
store
action
state 树
combineReducers
applyMiddleware()
Store
dispatch
subscribe 监听
getState()
dispatch()
getState()
getState()
dispatch()
dispatch()
dispatch()
dispatch()
dispatch()
dispatch()
dispatch()
dispatch()
dispatch()
写一个定制的 observeStore 工具
Observable
RxJS
Redux: Implementing Store from Scratch | egghead.io
filter()
Redux: Implementing Store from Scratch | egghead.io
Counter (cos)
Redux: React Counter example |egghead.io
Counter (cos)
filter()
请添加图片描述
请添加图片描述