# 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
- 排錯