• Redux 是個給 JavaScript 應用程式所使用的可預測 state 容器
  • 讓組件管理 state 更方便,且確保整個專案的資料都來自同一個地方
  • Flux 首先為 Facebook 提出的一個架構,用以方便管理 React 錯綜複雜的 state 及 action 關係;然而 Redux 則大幅簡化了 Flux 的概念。

Redux 三大原則:

  1. 唯一真相來源:整個應用程式的 state,被儲存在一個樹狀物件放在唯一的 store 裡面
  2. state 是唯讀的:改變 state 的唯一的方式是發出一個 action,也就是一個描述發生什麼事的物件
  3. 變更被寫成 pure function:要指定 state tree 如何藉由 action 來轉變,必須撰寫 pure reducer

Redux 三大元件:

  1. Action:
  • 從應用程式傳遞資料到 store 的資訊 (payload)。它們是 store 唯一的資訊來源。藉由 store.dispatch() 來把它們傳遞到 store。
  • Action 是一般的 JavaScript 物件。action 必須有一個 type 屬性,它代表被執行的 action 的類型。type 通常應該被定義成字串常數。
{
type: "COMPLETE_TODO",
payload: {
index: 1
}
}

2. Reducer:

  • reducer 是一個 pure function,它接收先前的 state 和一個 action,然後回傳下一個 state。
(previousState, action) => newState

透過給定的 state 跟 action ,可以得到新的 state。 Redux 為了讓事情單純,且可以回溯狀態,所以嚴格規定 reducer 是不能有副作用的 pure function,以確保給定相同的 state 和 action 會永遠得到相同的新 state。永遠不應該在 reducer 裡面做這些事:

  • 改變它的參數
  • 執行有 side effect 的動作,像是呼叫 API 和 routing 轉換
  • 呼叫不是 pure 的 function,像是 Date.now() 或是 Math.random()
const counter = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}

3. Store:store 有以下的責任:

  • 掌管應用程式狀態
  • 允許藉由 getState() 獲取 state
  • 允許藉由 dispatch(action) 來更新 state
  • 藉由 subscribe(listener) 註冊 listener
  • 藉由 subscribe(listener) 回傳的 function 處理撤銷 listener

store 需包含 reducer,如此才知道在發生某些 action 時,該怎麼做動得到目前該有的狀態(state)。所以將 reducer 引入 createStore 後,即可建立 store:

import { createStore } from 'redux' const store = createStore(reducer)

store 有以下三個最主要的方法:

  • store.getState():可以得到目前的狀態(state)
  • store.dispatch(action):發送一個動作給 store,store 會找到 reducer 執行後,得到新的 state
  • store.subscribe():向 store 訂閱 dispatch 事件,當 dispatch 發生時會執行訂閱的 callback (render)
import { createStore } from 'redux'

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

let store = createStore(counter)

store.subscribe(() =>
console.log(store.getState())
)

store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1

資料流:

  • Redux 架構圍繞著嚴格的單向資料流
  • 任何 Redux 應用程式中的資料生命週期都遵照這 4 個步驟:
  1. 呼叫 store.dispatch(action):action 是一個描述發生什麼事的一般物件。可以在應用程式的任何地方呼叫 store.dispatch(action),包括 component 和 XHR callback,或甚至在排程的 interval。
  2. Redux store 呼叫給它的 reducer function:store 會傳遞兩個參數到 reducer,現在的 state tree 和 action。記得 reducer 只是個 pure function。它只計算下一個 state。它應該是完全可以預測的:用相同的 inputs 多次呼叫它應該產生相同的 output。它不應該執行任何有 side effects 的動作,像是 API 呼叫或是 router transition。這些應該在 action 被 dispatch 之前發生。
function todos(state = [], action) {
// Somehow calculate it...
return nextState
}

function visibleTodoFilter(state = 'SHOW_ALL', action) {
// Somehow calculate it...
return nextState
}

let todoApp = combineReducers({
todos,
visibleTodoFilter
})

3. root reducer 可以把多個 reducer 的 output 合併成一個單一的 state tree:Redux 附帶一個 combineReducers() helper function,用於把 root reducer「拆分」成數個獨立 function,個別管理 state tree 的一個分支。

4. Redux store 儲存 root reducer 回傳的完整 state tree:這個新的 tree 現在是應用程式接下來的 state!每一個用 store.subscribe(listener) 註冊的 listener 現在將會被呼叫;listeners 可以呼叫 store.getState() 來取得現在的 state。

搭配 React 運用:

React 的單項資料流,不同子樹之間要交換資訊是相當麻煩的,使用 Redux 之後,元件要傳送資訊只要透過發出 action,要接收任意地方的資訊只要向 store 訂閱通知即可,元件也變得更好維護,也更有彈性。

React 需透過幾個步驟才能將 Redux 所保管的資料流向 component。首先像需求單一樣,定義要從 store 中取得的資料,並將 component 與該需求單做連結,之後再利用 Provider 將 store 根據需求單將資料流進 component 中。

  • 填寫需求單:創建一個函式 mapStateToProps ,並在該函式內部定義需要哪些資料,它有個參數 state,在連接時 Redux 會將 store 傳進這個位置,因此上方指定了 store 中保管的 name 資料,並以 name 為 key 從 props 流進 component 中。
const mapStateToProps = state => ({ name: state.name })
  • 創建組件:待會會與 mapStateToProps 進行連結,代表該 component 內含有從 store 中取的資料,而上方的 {this.props.name} 正是需求單上指定的名稱也就是 name
import React from 'react' class ConnectTitle extends React.Component {    
render(){
return <h1>Hello!{this.props.name}</h1>
}
}
  • 連結component與mapStateToProps:透過 connect 分別將 mapStateToProps 與 ConnectTitle 送進處理,會得到一個新的 component,並使用 Title 接收結果。
import { connect } from 'react-redux' const Title = connect(mapStateToProps)(ConnectTitle)
  • 設置Provider:Provider 是 react-redux 中的組件,它會接收上方在 Redux 中創建的 store,並根據和 component 綁在一起的需求單 mapStateToProps 上要求的資料從 store 中取出,再透過 props 流向 component。
import { connect, Provider } from 'react-redux'import ReactDOM from 'react-dom' ReactDOM.render(
<Provider store={store}>
<Title />
</Provider>,
document.getElementById('root')
)

Reference: