[Design Pattern] Flux pattern
為什麼我們需要 Flux?
問題:混亂的資料流向
在前端中,我們常用的 MVC 的功能只有幫我們做到關注點分離 (Separate of Concern; Soc),也就是幫我們把程式碼分成 Model, View, Controller 三個部分而已, 但並沒有限制我們的狀態要存在在哪,並且如何有規律的更改我們的狀態
在 MVC 中的每個部分都可以有自己的狀態,且任部分都可以更改任何位置的狀態,例如 Model 可以改 View 內的狀態,Controller 可以改 Model 的狀態
若沒有良好的慣例或維護,就會造成資料流難以掌控,後續專案難以維護
( 有維護過 jQuery 專案的就知道 🤪 )
所以,Flux pattern 就是提供一個概念讓大家可以遵循,讓大規模的開發和維護上不會有不知道資料流向的問題
什麼是 Flux?
讓 app 的資料流向遵循單一資料流 (Unidirectional data flow),以解決 MVC 沒有定義資料流向的,造成維護困難的問題
Flux pattern 就我的理解,可以分為兩個階段:
Singelton pattern:先產生一個全域的 Store,限制其存取的方式。 接者所有的 View 可以透過一個行動 (Action),並搭配特定的調動者 (Dispatcher),去改變指定的 Store 裡的狀態
ex:我們點擊登出按鈕,那我們就移除儲存在全域的登入狀態
Observer pattern:更新 View 時,在 Store state 更新時通知所有訂閱的 View(s)
ex:所有的頁面知道登入狀態改變了,就都會導回登入頁面
來達成單一資料流向,以下就來一一說明
Singleton: 限制改變 store 的 state 的方法
利用 Singleton Pattern 讓整個 app 的 global store 只有一個單一的存在,且只能透過指定的 action 去改變 store 的值,限制其存取,讓我們可以透過一個統一的 state 區塊去種整個資料流的更新
產生全局單一的 Store,並且限制其存取的方式
// Store's state
const initialState = { count: 0 };
// Store's 處理器 (Reducer): 根據 action 去改變 store 的 state
const countReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
}
const store = createStore(countReducer);
export store;
利用 action 被 dispatch 的方式,去改變 store 的 state
// Update store's state
import store from './store';
// Actions
const actions = {
increment: { type: 'INCREMENT' },
decrement: { type: 'DECREMENT' },
};
// Dispatcher dispatch action
store.dispatch(actions.increment);
// store's state changed
store.getState(); // { count: 1 }
Observer pattern: 通知所有的 views,store state 有變化了
在 Redux 的程式原始碼中,實現 dispatch
的部分,當利用 reducer 跟 action 產生新的 store state 時,就會通知所有註冊這個 store 的對象,通常是一個 view,或者 React 的一個 Component 等,這便是利用的 Observer pattern 的概念
const createStore = (myReducer) => {
let listener = [];
let currentState = myReducer(undefined, {});
...
const dispatch = (action) => {
currentState = myReducer(currentState, action);
listeners.forEach((listener) => {
listener();
});
};
return {
getState,
dispatch,
subscribe,
};
};
補充: 工廠模式 (Factory pattern) 來產生多個單體(Singleton)
在 Redux 中,沒有限制只能擁有一個 store,反而提供了一個 createStore
的 api,讓我們去建立自己的 store,這便是使用了 Factory pattern 的概念
Redux 利用 function programming 的 closure 技巧,來像 Class 一樣保存自己的狀態 & 自己的 listeners
const createStore = (myReducer) => {
let listener = [];
let currentState = myReducer(undefined, {});
const getState = () => {...};
const dispatch = (action) => {...};
const subscribe = (newListener) => {...};
return {
getState,
dispatch,
subscribe,
};
};
讓每個 store 都可以擁有自己的 Component, view listeners,自己的 action,且每個 store 都是獨立的,不會互相干擾
結論
- MVC 只是做 separation of concern,但是 Flux 靠著對資料來源的存取限制決定了資料的流動方式,讓我們更可以清楚地了解資料的流向,讓專案在 scaling 時更好維護
- 對 Singelton 的 store state 做存取的限制,只能用 action 改變,達成資料單一流向的目的
- 利用 Obeserver pattern 的概念,讓 store 通知所有的 View,通知所有註冊的 React component
參考資料
- The Mental Model That Helped Me Finally Understand "Flux" - Andy Ray's Blog (andrewray.me)
- A cartoon guide to Flux. Flux is both one of the most popular… | by Lin Clark | Code Cartoons | Medium
- Hacker Way: Rethinking Web App Development at Facebook - YouTube
- Flux 簡單範例程式 (openai.com)
- How to Use Flux to Manage State in ReactJS - Explained with an Example (freecodecamp.org)
- MVC架構是什麼?認識 Model-View-Controller 軟體設計模式 - ALPHA Camp