iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 18
1
Modern Web

激戰 ReactJS 30天系列 第 18

【Day18】Reducks? - Redux

前篇介紹了 Flux 的單向資料流架構模型
其實網路上有很多函式庫都在提供 API 實現 Flux 模型
其中有一個特別好用的
今天就要來了解這個資料流的函式庫 - Redux

Redux

Redux 是一套實現 Flux 模型的函式庫
和 React 本身並沒有直接關係
首先來看看 Redux 的資料流模型
https://ithelp.ithome.com.tw/upload/images/20180106/20107674CcQjaVmY7q.png
Redux 將 Flux 資料流模型再簡化成 View → Action → (middleware) → Reducer
由 View 偵測使用者互動行為
並且透過 Action Creator 來產生行為事件(Action)
Action 攜帶舊的狀態資料以及事件類型傳遞到 Reducer
若有中介軟體也會在這個時候先處理再傳遞給 Reducer
Reducer 核對 Action 的型態
並且就內部註冊的 API 進行處理
回傳新的 State 資料
最後 View 因為資料變動而更新畫面

Redux 和 Flux 相同的地方:

  1. 只能透過發送 Action 改變 Store 的資料(state)
  2. 資料的流動皆是單一方向
  3. 由四個不同的部分共同實現單一向資料流模型

Redux 和 Flux 不同的地方:

  1. Redux 只用一個 Store 以 物件樹 形式儲存資料 / Flux 具有多個分散的 Store 儲存不同狀態
  2. Flux 的 Dispatcher 在 Redux 中被 Reducer 取代

下面來分別探討 Redux 的四個部分:

Action

Redux 中的 Action 和 Flux 中的目的相同
都是為了讓 Store 知道資料需要被變動
通常會具有一個 type 供 Reducer 判斷行為是什麼
除此之外亦會帶有行為相關的資訊在裡面
例如下面這樣:

{
    type: ADD_SCORE,
    value:{
        num:'10'
    }
}

Actions 皆是由 Action Creator 負責發布出去
當 View 接收到使用者互動行為後
會通知 Action Creator 發布行為
在原生的 Flux 中會需要做**調度(dispatch)**的動作

function addScoreWithDispatch(num) {
    dispatch({
        type: ADD_SCORE,
        value:{
            num
        }
    });
}
// 實際發送 Action
addScoreWithDispatch(num)

但是在 Redux 我們只需要單純回傳 Action 這個物件就可以了

function addScore(num) {
    return {
        type: ADD_SCORE,
        value:{
            num
        }
    };
}
// 實際發送 Action
dispatch(addScore(num));

Reducer

和 Flux 中的 Stores 類似
是用來針對 Actions 做出反應的部分
根據傳入的 Action Type
將同步傳入的舊狀態資料經過處理後
回傳一個新的狀態資料到 Store 中

function changeScore(state = 0, action){
    switch(action.type){
        case ADD_SCORE:
            return state + action.value;
        case SUB_SCORE:
            return state - action.value;
        default:
            return state;
    }
} 

上面的例子就是一個簡單的 Reducer
根據行為的action.type來改變狀態資料
Reducer 的參數規格就是狀態資料行為物件
(state, action) => newState
state 參數並沒有型態的限制
但是不能直接修改原始的資料
應透過回傳新的狀態資料來更新

Store

Redux 只有一個 Store
因此 Reducer 要負責處理所有行為和狀態的應對及變化
我們可以透過 API 建立 Store
使用前需要先引入
import { createStore } from 'redux';
這樣才能夠使用 createStore 函式去產生 Store
產生 Store 的時候我們需要給他負責協調處理的 Reducer
產生出來的 Store 是一個物件
他長成這樣:

{
    dispatch,
    subscribe,
    getState,
    getReducer,
    replaceReducer
};

裡面裝的是他可以使用的相關函式
dispatch是發布 Action 的函式
是改變狀態資料唯一途徑
subscribe是添加監聽器的函式
當有 Action 被發布的時候就會被執行
getState可以取得當前的狀態資料樹
與最後一次的 Reducer 返回值相同
getReducer可以取得當前使用的 Reducer
replaceReducer用來替換 Store 目前的 Reducer
通常在需要新增 Reducer 的時候使用

看到這邊
相信如果有新手跟我一樣會覺得
這到底在供三小?
上面這些跟前面十幾天寫的東西怎麼感覺沒關係?
經過我的消化吸收後
只能說
欸斗 資料流是一個模式
換句話說他並不是像 API 只要調用就發揮作用
得要靠寫程式的我們自己去把它使用來編寫
所以說要怎麼把這些東西跟我們的 React 連用起來咧
首先我們要知道
Redux 資料流的幾個部分
到 React 中得要依賴組件來實現
透過組件或函式之間的運作來達到單項的資料流動
那麼要如何連接組件讓 Redux 運作起來?
首先要先安裝 react-redux 這個東西

透過在 cmd 下指令安裝:
npm install react-redux --save

react-redux 提供兩個方法 - providerconnect
provider負責讓組件成為可連結的元件
並且將唯一 Store 階層傳遞下去

const store = createStore(reducer);

class App extends Component {
    render() {
        return (
            <Provider store={store}>
                <App />
            </Provider>
        );
    }
}

而階層下需要接收狀態資料的組件必須被 connect
connect 會接收一個函式參數
這個函式可以讓組件篩選需要使用到哪些狀態資料
並且回傳一個全新的組件

function select(state) {
    return {
        score : state.score
    };
}

class ScoreBoard extends Component {
  render(){
    return <div>{this.props.score}</div>
  }
}

export default connect(select)(ScoreBoard)

參考資料

  1. tutorialspoint-ReactJS Tutorial
  2. React 官方文件

>>> 隊友任意門 <<<


Day18 end
by 瑞Ray ✧◝(⁰▿⁰)◜✧


上一篇
【Day17】 從哪來到哪去 - Data Flow
下一篇
【Day19】 見證奇蹟的時刻 - 實作Redux
系列文
激戰 ReactJS 30天31

尚未有邦友留言

立即登入留言