# Redux 常见问题:不可变对象
## 目录
- [不变性(immutability)的好处有哪些?](#benefits-of-immutability)
- [为什么 Redux 需要不变性?](#why-is-immutability-required)
- [为什么 Redux 对浅比较的使用要求不变性?](#redux-shallow-checking-requires-immutability) - [浅比较和深比较有何区别?](#shallow-and-deep-equality-checking) - [Redux 是如何使用浅比较的?](#how-redux-uses-shallow-checking) - [`combineReducers` 是如何进行浅比较的?](#how-combine-reducers-uses-shallow-checking) - [React-Redux 是如何使用浅比较拗的?](#how-react-redux-uses-shallow-checking) - [React-Redux 是如何使用浅比较来决定组件是否需要重新渲染的?](#how-react-redux-determines-need-for-re-rendering) - [为什么在使用可变对象时不能用浅比较?](#no-shallow-equality-checking-with-mutable-objects) - [使用浅比较检查一个可变对象对 Redux 会造成问题吗?](#shallow-checking-problems-with-redux)
- [为什么 reducer 直接修改 state 会导致 React-Redux 不重新渲染包装的组件?](#shallow-checking-problems-with-react-redux) - [为什么 `mapStateToProps` 的 selector 直接修改并返回一个对象时,React-Redux 包装的组件不会重新渲染?](#shallow-checking-stops-component-re-rendering) - [“不变性”如何使得浅比较检测到对象变化的?](#immutability-enables-shallow-checking)
- [reducer 中的不变性是如何导致组件非必要渲染的?](#immutability-issues-with-redux)
- [mapStateToProps 中的不变性是如何导致组件非必要渲染的?](#immutability-issues-with-react-redux)
- [处理不可变数据都有哪些途径?一定要用 Immutable.JS 吗?](#do-i-have-to-use-immutable-js)
- [原生 JavaScript 进行不可变操作会遇到哪些问题?](#issues-with-es6-for-immutable-ops)
<a id="benefits-of-immutability"></a>
## 不变性的好处有哪些
不变性可以给你的应用带来性能提升,也可以带来更简单的编程和调试体验。这是因为,与那些在整个应用中可被随意篡改的数据相比,永远不变的数据更容易追踪,推导。
特别来说,在 Web 应用中对于不变性的使用,可以让复杂的变化检测机制得以简单快速的实现。从而确保代价高昂的 DOM 更新过程只在真正需要的时候进行(这也是 React 性能方面优于其他类库的基石)。
#### 更多信息
**文章**
- [Introduction to Immutable.js and Functional Programming Concepts](https://auth0.com/blog/intro-to-immutable-js/)
- [JavaScript Immutability presentation (PDF - see slide 12 for benefits)](https://www.jfokus.se/jfokus16/preso/JavaScript-Immutability--Dont-Go-Changing.pdf)
- [Immutable.js - Immutable Collections for JavaScript](https://facebook.github.io/immutable-js/#the-case-for-immutability)
- [React: Optimizing Performance](https://facebook.github.io/react/docs/optimizing-performance.html)
- [JavaScript Application Architecture On The Road To 2015](https://medium.com/google-developers/javascript-application-architecture-on-the-road-to-2015-d8125811101b#.djje0rfys)
<a id="why-is-immutability-required"></a>
## 为什么 Redux 需要不变性?
- Redux 和 React-Redux 都使用了[浅比较](#shallow-and-deep-equality-checking)。具体来说:
- Redux 的 `combineReducers` 方法 [浅比较](#how-redux-uses-shallow-checking) 它调用的 reducer 的引用是否发生变化。
- React-Redux 的 `connect` 方法生成的组件通过 [浅比较根 state 的引用变化](#how-react-redux-uses-shallow-checking) 与 `mapStateToProps` 函数的返回值,来判断包装的组件是否需要重新渲染。
以上[浅比较需要不变性](#redux-shallow-checking-requires-immutability)才能正常工作
- 不可变数据的管理极大地提升了数据处理的安全性。
- 进行时间旅行调试要求 reducer 是一个没有副作用的纯函数,以此在不同 state 之间正确的移动。
#### 更多信息
**文档**
- [技巧: Reducer 基础概念](http://cn.redux.js.org/docs/recipes/reducers/PrerequisiteConcepts.html)
**讨论**
- [Reddit: Why Redux Needs Reducers To Be Pure Functions](https://www.reddit.com/r/reactjs/comments/5ecqqv/why_redux_need_reducers_to_be_pure_functions/dacmmjh/?context=3)
<a id="redux-shallow-checking-requires-immutability"></a>
## 为什么 Redux 对浅比较的使用要求不变性?
Redux 对浅比较的使用要求不变性,以保证任何连接的组件能被正确渲染。要了解原因,我们需要理解 Javascript 中浅比较和深比较的区别。
<a id="shallow-and-deep-equality-checking"></a>
### 浅比较和深比较有何区别?
浅比较(也被称为 **引用相等**)只检查两个不同 **变量** 是否为同一对象的引用;与之相反,深比较(也被称为 **原值相等**)必须检查两个对象所有属性的 **值** 是否相等。
所以,浅比较就是简单的(且快速的)`a === b`,而深比较需要以递归的方式遍历两个对象的所有属性,在每一个循环中对比各个属性的值。
正是因为性能考虑,Redux 使用浅比较。
#### 更多信息
**文章**
- [Pros and Cons of using immutability with React.js](http://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/)
<a id="how-redux-uses-shallow-checking"></a>
### Redux 是如何使用浅比较的?
Redux 在 `combineReducers` 函数中使用浅比较来检查根 state 对象(root state object)是否发生变化,有修改时,返回经过修改的根 state 对象的拷贝,没有修改时,返回当前的根 state 对象。
#### 更多信息
**文档**
- [API 文档: combineReducers](http://cn.redux.js.org/docs/api/combineReducers.html)
<a id="how-combine-reducers-uses-shallow-checking"></a>
#### `combineReducers` 是如何进行浅比较的?
Redux 中 store [推荐的结构](http://cn.redux.js.org/docs/faq/Reducers.html#reducers-share-state) 是将 state 对象按键值切分成 “层”(slice) 或者 “域”(domain),并提供独立的 reducer 方法管理各自的数据层。
`combineReducers` 接受 `reducers` 参数简化了该模型。`reducers` 参数是一组键值对组成的哈希表,其中键是每个数据层的名字,而相应的值是响应该数据层的 reducer 函数。
举例说明,如果你的 state 结构是 `{ todos, counter }`,调用 `combineReducers` 即:
```js
combineReducers({ todos: myTodosReducer, counter: myCounterReducer })
```
其中:
- `todos` 和 `counter` 两个键各自是不同的 state 层。
- `myTodosReducer` 和 `myCounterReducer` 两个值是 reducer 函数,各自负责处理它们的键所对应的 state 层。
`combineReducers` 遍历所有这些键值对,对于每一次循环:
- 为每一个键代表的当前 state 层创建一个引用;
- 调用相应的 reducer 并把该数据层传递给它
- 为 reducer 返回的可能发生了变化的 state 层创建一个引用。
在循环过程中,对于每一个 reducer 返回的 state 层,`combineReducers` 都会根据其创建一个新的 state 对象。这个新的 state 对象与当前 state 对象可能有区别,也可能没有区别。于是在这里 `combineReducers` 使用浅比较来判断 state 到底有没有发生变化。
特别来说,在循环的每一阶段,`combineReducers` 会浅比较当前 state 层与 reducer 返回的 state 层。如果 reducer 返回了新的对象,它们就不是浅相等的,而且 `combineReducers` 会把 `hasChanged` 设置为 true。
循环结束后,`combineReducers` 会检查 `hasChanged` 的值,如果为 true,就会返回新构建的 state 对象。如果为 false,就会返回**当前**state 对象。
需要强调的一点是:**如果所有 reducer 返回的 `state` 对象都与传入时一致,那么 `combineReducers` 将返回当前的根 state 对象,而不是新构建的。**
#### 更多信息
**文档**
- [API 文档: combineReducers](http://cn.redux.js.org/docs/api/combineReducers.html)
- [常见问题 - 如何在 reducer 之间共享 state? `combineReducers` 是必须的吗?](http://cn.redux.js.org/docs/faq/Reducers.html#reducers-share-state)
**视频**
- [Egghead.io: Redux: Implementing combineReducers() from Scratch](https://egghead.io/lessons/javascript-redux-implementing-combinereducers-from-scratch)
<a id="how-react-redux-uses-shallow-checking"></a>
### React-Redux 是如何使用浅比较的?
React-Redux 使用浅比较来决定它包装的组件是否需要重新渲染。
首先 React-Redux 假设包装的组件是一个“纯”(pure)组件,即[给定相同的 props 和 state,这个组件会返回相同的结果](https://github.com/reactjs/react-redux/blob/f4d55840a14601c3a5bdc0c3d741fc5753e87f66/docs/troubleshooting.md#my-views-arent-updating-when-something-changes-outside-of-redux)。
做出这样的假设后,React-Redux 就只需检查根 state 对象或 `mapStateToProps` 的返回值是否改变。如果没变,包装的组件就无需重新渲染。
为了检测改变是否发生,React-Redux 会保留一个对根 state 对象的引用,还会保留 `mapStateToProps` 返回的 props 对象的**每个值**的引用。
最后 React-Redux 会对根 state 对象的引用与传递给它的 state 对象进行浅比较,还会对每个 props 对象的每个值的引用与 `mapStateToProps` 返回的那些值进行一系列浅比较。
#### 更多信息
**文档**
- [搭配 React](http://cn.redux.js.org/docs/basics/UsageWithReact.html)
**文章**
- [API: React-Redux’s connect function and `mapStateToProps`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options)
- [Troubleshooting: My views aren’t updating when something changes outside of Redux](https://github.com/reactjs/react-redux/blob/f4d55840a14601c3a5bdc0c3d741fc5753e87f66/docs/troubleshooting.md#my-views-arent-updating-when-something-changes-outside-of-redux)
### 为什么 React-Redux 对 `mapStateToProps` 返回的 props 对象的每个值进行浅比较?
对 props 对象来说,React-Redux 会对其中的每个**值**进行浅比较,而不是 props 对象本身。
它这样做的原因是:props 对象实际上是一组由属性名和其值(或用于取值或生成值的 selector 函数)的键值对组成的。请看下例:
```js
function mapStateToProps(state) {
return {
todos: state.todos, // prop value
visibleTodos: getVisibleTodos(state) // selector
}
}
export default connect(mapStateToProps)(TodoApp)
```
像这样,重复调用 `mapStateToProps` 每次返回的 props 对象都不是浅层相等的,因为 `mapStateToProps` 总是会返回新的对象。
#### 更多信息
**文章**
- [React.js pure render performance anti-pattern](https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.gh07cm24f)
<a id="how-react-redux-determines-need-for-re-rendering"></a>
### React-Redux 是如何使用浅比较来决定组件是否需要重新渲染的?
每次调用 React-Redux 提供的 `connect` 函数时,它储存的根 state 对象的引用,与当前传递给 store 的根 state 对象之间,会进行浅比较。如果相等,说明根 state 对象没有变化,也就无需重新渲染组件,甚至无需调用 `mapStateToProps`。
如果发现其不相等,说明根 state 对象**已经**被更新了,这时 `connect` 会调用 `mapStateToProps` 来查看传给包装的组件的 props 是否被更新。
它会对该对象的每一个值各自进行浅比较,如果发现其中有不相等的才会触发重新渲染。
在下例中,调用 `connect` 后,如果 `state.todos` 以及 `getVisibleTodos()` 的返回值没有改变,组件就不会重新渲染。
```js
function mapStateToProps(state) {
return {
todos: state.todos, // prop value
visibleTodos: getVisibleTodos(state) // selector
}
}
export default connect(mapStateToProps)(TodoApp)
```
与之相反,在下例中,组件**总是**重新渲染,因为不管 `todos` 的值有没有改变,`todos` 本身总是一个新的对象。
```js
// AVOID - will always cause a re-render
function mapStateToProps(state) {
return {
// todos always references a newly-created object
todos: {
all: state.todos,
visibleTodos: getVisibleTodos(state)
}
}
}
export default connect(mapStateToProps)(TodoApp)
```
`mapStateToProps` 返回的新值,与 React-Redux 保留的旧值的引用如果不是浅层相等的,组件就会被重新渲染。
#### 更多信息
**文章**
- [Practical Redux, Part 6: Connected Lists, Forms, and Performance](http://blog.isquaredsoftware.com/2017/01/practical-redux-part-6-connected-lists-forms-and-performance/)
- [React.js Pure Render Performance Anti-Pattern](https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.sb708slq6)
- [High Performance Redux Apps](http://somebody32.github.io/high-performance-redux/)
**讨论**
- [#1816: Component connected to state with `mapStateToProps`](https://github.com/reactjs/redux/issues/1816)
- [#300: Potential connect() optimization](https://github.com/reactjs/react-redux/issues/300)
<a id="no-shallow-equality-checking-with-mutable-objects"></a>
### 为什么在使用可变对象时不能用浅比较?
如果一个函数改变了传给它的可变对象的值,这时就不能使用浅比较。
这是因为对同一个对象的两个引用**总是**相同的,不管此对象的值有没有改变,它们都是同一个对象的引用。因此,以下这段代码总会返回 true:
```js
function mutateObj(obj) {
obj.key = 'newValue'
return obj
}
const param = { key: 'originalValue' }
const returnVal = mutateObj(param)
param === returnVal
//> true
```
`param` 与 `returnValue` 的浅比较只是检查了这两个对象是否为相同对象的引用,而这段代码中总是(相同的对象的引用)。`mutateObj()` 也许会改变 `obj`,但它仍是传入的对象的引用。浅比较根本无法判断 `mutateObj` 改变了它的值。
#### 更多信息
**文章**
- [Pros and Cons of using immutability with React.js](http://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/)
<a id="shallow-checking-problems-with-redux"></a>
### 使用浅比较检查一个可变对象对 Redux 会造成问题吗?
对于 Redux 来说,使用浅比较来检查可变对象不会造成问题,但[当你使用依赖于 store 的类库时(例如 React-Redux),就会造成问题](#shallow-checking-problems-with-react-redux)。
特别是,如果 `combineReducers` 传给某个 reducer 的 state 层是一个可变对象,reducer 就可以直接修改数据并返回。
这样一来,浅比较判断 `combineReducers` 总会相等。因为尽管 reducer 返回的 state 层可能被修改了,但这个对象本身没有,它仍是传给 reducer 的那个对象。
从而,尽管 state 发生了变化,`combineReducers` 不会改变 `hasChanged` 的值。如果所有 reducer 都没有返回新的 state 层,`hasChange` 就会始终是 false,于是 `combineReducers` 就返回**现有的**根 state 对象。
store 仍会根据新的根 state 对象进行更新,但由于根 state 对象仍然是同一个对象,绑定于 Redux 的类库(例如 React-Redux)不会觉察到 state 的变化,于是不会触发包装组件的重新渲染。
#### 更多信息
**文档**
- [技巧: 不可变更新模式](http://cn.redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html)
- [排错: 永远不要直接修改 reducer 的参数](http://cn.redux.js.org/docs/Troubleshooting.html#never-mutate-reducer-arguments)
<a id="shallow-checking-problems-with-react-redux"></a>
### 为什么 reducer 直接修改 state 会导致 React-Redux 不重新渲染包装的组件?
如果某个 Redux 的 reducer 直接修改并返回了传给它的 state 对象,那么根 state 对象的值的确会改变,但这个对象自身的引用没有变化。
React-Redux 对根 state 对象进行浅比较,来决定是否要重新渲染包装的组件,因此它不会检测到 state 的变化,也就不会触发重新渲染。
#### 更多信息
**文档**
- [Troubleshooting: My views aren’t updating when something changes outside of Redux](https://github.com/reactjs/react-redux/blob/f4d55840a14601c3a5bdc0c3d741fc5753e87f66/docs/troubleshooting.md#my-views-arent-updating-when-something-changes-outside-of-redux)
<a id="shallow-checking-stops-component-re-rendering"></a>
### 为什么 `mapStateToProps` 的 selector 直接修改并返回一个对象时,React-Redux 包装的组件不会重新渲染?
如果 `mapStateToProps` 返回的 props 对象的值当中,有一个每次调用 `connect` 时都不会发生改变的对象(比如,有可能是根 state 对象),同时还是一个 selector 函数直接改变并返回的对象,那么 React-Redux 就不会检测到这次改变,也就不会触发包装的组件的重新渲染。
我们已经知道了,selector 函数返回的可变对象中的值也许改变了,但这个对象本身没有。浅比较只会检查两个对象自身,而不会对比它们的值。
比如说,下例中 `mapStateToProps` 函数永远不会触发重新渲染:
```js
// store 中的 state 对象
const state = {
user: {
accessCount: 0,
name: 'keith'
}
}
// selector 函数
const getUser = state => {
++state.user.accessCount // mutate the state object
return state
}
// mapStateToProps
const mapStateToProps = state => ({
// getUser() 返回的对象总是同一个对象,
// 所以这个包装的组件永远不会重新渲染,
// 尽管它已经被改变了
userRecord: getUser(state)
})
const a = mapStateToProps(state)
const b = mapStateToProps(state)
a.userRecord === b.userRecord
//> true
```
注意,与之相反,如果使用了一个**不可变**对象,组件可能会在不该渲染时重新渲染。
#### 更多信息
**文章**
- [Practical Redux, Part 6: Connected Lists, Forms, and Performance](http://blog.isquaredsoftware.com/2017/01/practical-redux-part-6-connected-lists-forms-and-performance/)
**讨论**
- [#1948: Is getMappedItems an anti-pattern in mapStateToProps?](https://github.com/reactjs/redux/issues/1948)
<a id="immutability-enables-shallow-checking"></a>
### “不变性”如何使得浅比较检测到对象变化的?
如果某个对象是不可变的,那么一个函数需要对它进行改变时,就只能改变它的 **拷贝**。
这个被改变了的拷贝与原先传入该函数的对象**不是同一个对象**,于是当它被返回时,浅比较检查就会知道它与传入的对象不同,于是就判断为不相等。
#### 更多信息
**文章**
- [Pros and Cons of using immutability with React.js](http://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/)
<a id="immutability-issues-with-redux"></a>
### reducer 中的不变性是如何导致组件非必要渲染的?
你不能直接修改某个对象,你只能修改它的拷贝,并保持原对象不变。
修改拷贝完全不会造成问题。但在一个 reducer 里,如果你返回了一个**没有进行**任何修改、与原对象一模一样的拷贝,Redux 的 `combineReducers` 函数仍会认为 state 需要更新,因为你返回了一个与传入的 state 对象完全不同的对象。
`combineReducers` 会把这个新的根 state 对象返回给 store。新的对象与原有的根 state 对象的值是相同的,但由于对象本身不同,会导致 store 更新,从而所有已连接的组件都进行了毫无必要的重新渲染。
为了防止这种现象的发生,**当 reducer 没有改变 state 时,你必须直接返回的传入的 state 层。**
#### 更多信息
**文章**
- [React.js pure render performance anti-pattern](https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.5hmnwygsy)
- [Building Efficient UI with React and Redux](https://www.toptal.com/react/react-redux-and-immutablejs)
<a id="immutability-issues-with-react-redux"></a>
### mapStateToProps 中的不变性是如何导致组件非必要渲染的?
某些特定的不可变操作,比如数组的 filter,总会返回一个新的对象,即使这些值没有改变。
如果在 `mapStateToProps` 的 selector 函数中使用了这样的操作,那么 React-Redux 使用浅比较检查返回的 props 的值时就会认为不相等,因为 selector 每次都返回了一个新的对象。
这样一来,即使新的对象的所有值都没有改变,包装的组件也会重新渲染。
```js
// JavaScript 数组的“filter”方法认为该数组是不可变的
// 于是返回数组被 filter 后的拷贝
const getVisibleTodos = todos => todos.filter(t => !t.completed)
const state = {
todos: [
{
text: 'do todo 1',
completed: false
},
{
text: 'do todo 2',
completed: true
}
]
}
const mapStateToProps = state => ({
// getVisibleTodos() 总会返回新的数组,所以
// “visibleToDos” 属性一直会指向不同的数组,
// 结果是即使数组的值没有改变,包装的组件也会重新渲染
visibleToDos: getVisibleTodos(state.todos)
})
const a = mapStateToProps(state)
// 用完全相同的参数再次调用 mapStateToProps(state)
const b = mapStateToProps(state)
a.visibleToDos
//> { "completed": false, "text": "do todo 1" }
b.visibleToDos
//> { "completed": false, "text": "do todo 1" }
a.visibleToDos === b.visibleToDos
//> false
```
注意,与之相反,如果你的 props 对象中的值是可变对象,[组件可能在需要渲染时也不渲染](#shallow-checking-stops-component-re-rendering)。
#### 更多信息
**文章**
- [React.js pure render performance anti-pattern](https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f#.b8bpx1ncj)
- [Building Efficient UI with React and Redux](https://www.toptal.com/react/react-redux-and-immutablejs)
- [ImmutableJS: worth the price?](https://medium.com/@AlexFaunt/immutablejs-worth-the-price-66391b8742d4#.a3alci2g8)
<a id="do-i-have-to-use-immutable-js"></a>
## 处理不可变数据都有哪些途径?一定要用 Immutable.JS 吗?
你不一定要与 Redux 一起使用 Immutable.JS。原生 JavaScript,如果书写得当,是足以达到所需的不变性,不需要使用强制不可变的类库。
但是,在 JavaScript 中保证不变性是很难的。不小心直接修改了一个对象反而很简单,这就会导致你的应用中出现极难以调试的 bug。因此,使用一个提供不可变性的类库(比如 Immutable.JS)会显著提高你的应用的可靠性,而且让你的开发更为便捷
#### 更多信息
**讨论**
- [#1185: Question: Should I use immutable data structures?](https://github.com/reactjs/redux/issues/1422)
- [Introduction to Immutable.js and Functional Programming Concepts](https://auth0.com/blog/intro-to-immutable-js/)
<a id="issues-with-es6-for-immutable-ops"></a>
## 原生 JavaScript 进行不可变操作会遇到哪些问题?
JavaSctipt 从不是为了确保不可变性而设计的。所以,有几点事项是你需要特别留意的,如果你准备在 Redux 应用中使用不可变操作的话。
### 不小心直接修改了对象
使用 JavaScript 时,你很容易一不小心直接修改了一个对象(比如 Redux 中的 state 树),甚至自己都没意识到。比如说,更新了多层嵌套中的属性、给一个对象创建了一个**引用**而不是创建一个新的对象、或者用了浅拷贝而不是深拷贝,这些都会导致非故意的对象修改,甚至经验丰富的 JavaScript 程序员都会犯此错误。
为了避免这些问题,请确保你遵守了推荐的 [不可变更新模式](http://cn.redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html)。
### 重复代码
更新复杂的多级嵌套的 state 树会导致重复代码的出现,这样的代码不但写起来无趣,维护起来也很困难。
### 性能问题
用不可变的方式操作 JavaScript 的对象和数组可能会很慢,特别是你的 state 树很大的时候。
记住,想要改变一个不可变对象,你必须只修改其**拷贝**,而拷贝庞大的对象可能会很慢,因为每一个属性都需要拷贝。
不过,像 Immutable.JS 这样提供不可变性的类库会进行复杂精妙的优化,比如 [结构共享](http://www.slideshare.net/mohitthatte/a-deep-dive-into-clojures-data-structures-euroclojure-2015)),它能够在返回新对象的同时复用原有对象的结构,从而更加高效地实现拷贝。
对于非常庞大的对象,原生 JavaScript 比经过优化的不可变类库 [慢 100 倍](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.z1g1ofrsi)。
#### 更多信息
**文档**
- [不可变更新模式](http://cn.redux.js.org/docs/recipes/reducers/ImmutableUpdatePatterns.html)
**文章**
- [Immutable.js, persistent data structures and structural sharing](https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2#.a2jimoiaf)
- [A deep dive into Clojure’s data structures](http://www.slideshare.net/mohitthatte/a-deep-dive-into-clojures-data-structures-euroclojure-2015)
- [Introduction to Immutable.js and Functional Programming Concepts](https://auth0.com/blog/intro-to-immutable-js/)
- [JavaScript and Immutability](http://t4d.io/javascript-and-immutability/)
- [Immutable Javascript using ES6 and beyond](http://wecodetheweb.com/2016/02/12/immutable-javascript-using-es6-and-beyond/)
- [Pros and Cons of using immutability with React.js - React Kung Fu](http://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/)
- 自述
- 介绍
- 动机
- 核心概念
- 三大原则
- 先前技术
- 学习资源
- 生态系统
- 示例
- 基础
- Action
- Reducer
- Store
- 数据流
- 搭配 React
- 示例:Todo List
- 高级
- 异步 Action
- 异步数据流
- Middleware
- 搭配 React Router
- 示例:Reddit API
- 下一步
- 技巧
- 配置 Store
- 迁移到 Redux
- 使用对象展开运算符
- 减少样板代码
- 服务端渲染
- 编写测试
- 计算衍生数据
- 实现撤销重做
- 子应用隔离
- 组织 Reducer
- Reducer 基础概念
- Reducer 基础结构
- Reducer 逻辑拆分
- Reducer 重构示例
- combineReducers 用法
- combineReducers 进阶
- State 范式化
- 管理范式化数据
- Reducer 逻辑复用
- 不可变更新模式
- 初始化 State
- 结合 Immutable.JS 使用 Redux
- 常见问题
- 综合
- Reducer
- 组织 State
- 创建 Store
- Action
- 不可变数据
- 代码结构
- 性能
- 设计哲学
- React Redux
- 其它
- 排错
- 词汇表
- API 文档
- createStore
- Store
- combineReducers
- applyMiddleware
- bindActionCreators
- compose
- react-redux 文档
- API
- 排错