ReactJS

何時該使用 React Redux

今天這章會討論什麼時候要把資料放在 Redux 裡面,什麼時候要將資料放在 React 的 components 裡。

React 將 components 分成 Presentational Components (展示組件) 與 Container Components (容器組件)。

Presentational Components

Presentational Components 主要負責 UI 的部分,通常不會有複雜 application 狀態管理的資料,通過會由 props 傳入無狀態 (stateless) 的資料,像是:

  • 跟 UI 相關的資料 e.g. 顏色的使用
  • 只有在必要時才擁有自己的狀態 e.g. 下拉式選單的狀態
  • 需要手動建立新的東西 e.g. new post 的資料儲存

Container Components

Container Components 主要負責 application 的狀態,通常會使用 Redux 來管理,然後在 render 的時候會資料傳給 Presentational Components,通常會儲存有狀態的資料:

  • application data flow
  • 使用者相關的資訊 e.g. 最喜歡的顏色

Compare

特性 Presentational Container
主要用途 UI render application state
狀態 無狀態或是透過 props 獲得狀態 通常有狀態,透過 Redux 管理
類別 smart dumb
儲存類型 儲存複雜的東西 儲存簡單的東西

Reference

React Redux Example App 範例程式碼

前一篇介紹 Redux 是參考 Flux 的架構而設計的,這篇要用 Redux 來寫一個簡單的 React Counter App。

在 Ubuntu 18.04 上安裝 Docker CE

再提醒一下,React 跟 Redux 的差別是:React 是一個 front-end library;Redux 是一個架構,可以不用跟 React 一起使用,也可以跟 Vue 或 Angular 搭配。

Counter App

Install

建立一個 React App 然後安裝 Redux:

1
2
3
npx create-react-app counter-app
cd counter-app
npm install --save redux react-redux

Actions

建立 Redux 的 Action,Action 一定要回傳 type 讓 Reducer 去做對應的資料更新,Middleware 會在送到 Reducer 之前執行。

1
2
3
4
5
6
7
8
// actions.js
export const increment = () => ({
type: 'INCREMENT'
});

export const decrement = () => ({
type: 'DECREMENT'
});

Reducer

建立 Redux 的 Reducer,Reducer 不能修改原本的 state,必須複製一份,並且回傳最後 state 應該要的資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// reducer.js
const initialState = {
count: 0
};

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

export default counterReducer;

Store

建立 Redux 的 Store,用來儲存 state 的資料

1
2
3
4
5
6
7
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';

const store = createStore(counterReducer);

export default store;

Counter

建立 React Counter Element,而且注意這邊需要將 Action 傳入 Dispatcher 來讓後續的 Reducer 來更新狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions';

const Counter = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();

return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
};

export default Counter;

App

最後 react-redux library 透過 Provider Component 來跟 React 串接,Provider 會提供 Store 的資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';

const App = () => (
<Provider store={store}>
<Counter />
</Provider>
);

export default App;

Reference

Flux 跟 Redux 之間的關係

Flux 和 Redux 都是用來管理 application 狀態的架構,差異是不同的設計理念和實做方式:

Flux

Flux 是 Facebook 提出的架構模式,用於解決複雜的 data flow 問題,Flux 的架構如下:

  1. Action: 描述 application 的事件或行為。
  2. Dispatcher: 分發 Action 給 Store。它是 Flux 架構中的中央樞紐。
  3. Store: 儲存 application 狀態和邏輯。每個 Store 負責 application 一部分狀態。
  4. View: 展示 application UI,並且可以根據 Store 的變化來更新。

Flux 的 data flow 是單向的,從 Action 到 Dispatcher,再到 Store,最後到 view。

Redux

Redux 參考並簡化 Flux 管理狀態概念,Redux 的架構如下:

  1. Action: 與 Flux 中的 Action 類似,用於描述 application 的事件或行為。
  2. Reducer: 是一個 function,負責根據 Action 來更新 application 的狀態。Redux 中沒有 Sispatcher,取而代之的是 Reducer。
  3. Store: 儲存 application 的狀態。Redux 中只有一個單一的 Store,與 Flux 有多個 Store 不同。而且 Redux 只能透過 Action 可以修改 Store 的資料。
  4. Middleware: 用於處理異步操作或其他事件 e.g. Error Handling。

Redux 的 data flow 也是單向的,並且強調使用 function 來更新狀態。

Redux 使用單一集中狀態的物件,並以特定的方式進行更新。當你想要更新狀態時(e.g click event),會創建一個 Action 並由某個 Reducer 處理。Reducer 會複製當前狀態,且使用 Action 中的資料進行修改,然後返回新的狀態。當 Store 更新時,可以監聽事件並更新

Redux 跟 React 的差別是:React 是一個 front-end library;Redux 是一個架構,可以不用跟 React 一起使用,也可以跟 Vue 或 Angular 搭配。這篇是一個 React Redux 的範例:

React Redux Example App 範例程式碼

Compare

  • Redux 使用單一的 store: 與 Flux 在中多個 Store 中定位狀態信息不同,Redux 將所有內容保存在一個地方。在 Flux 中,可以有許多不同的 store。Redux 打破了這一點,強制使用單一 global Store。
  • Redux 使用 reducers: Reducers 是以不更動原本資料的方式來更新資料。在 Redux 中,行為是以可預測的,因為所有的改動都要經過 Reducer,且每一次的改動只會更新一次 global Store。
  • Redux 使用 middleware: 由於 Action 和資料以單向方式流動,我們可以透過 Redux 增加 middleware,並在資料更新時加上客製化的行為 e.g. Log or Catch Error。
  • Redux decouple Action 與 Store: 建立 Action 時不會向通知 Store 任何東西,反而是回傳 Action 物件;Flux 的 Action 則會直接修改 Store。

Reference