React Redux 用過嗎
Redux 對專案帶來的好處:
-
讓組件管理
state更方便,且確保整個專案的資料都來自同一個地方。 -
開發時能讓畫面及資料端分離,有關畫面的就修改
React的component,資料的部分就完全交給Redux,每個人各司其職、分工合作。 -
承上點,若是單人作業,也許會認為:「那我一個人,還有需要分開寫嗎?太麻煩了吧!」,不過就是因為一個人,將來維護時,如果發現問題就更不需要再花費時間,讓自己漫遊在一堆
component、fetch或ajax的程式中尋找問題在哪裡,對吧?
Redux 基本用法
在講解兩者如何配合前,還是得先了解 Redux 的運作模式,九把刀說過:「慢慢來,比較快。」,所以一步一步來走吧!
store:儲存全域State
action:改變State的行為
reducer:負責去確認現在要用哪個action,進而執行並且改變State
dispatch:傳送action給reducer
創建 Reducer
Reducer 在 Redux 中是用來保管 state ,以及在接收到不同的 action 指令時該對 state 做什麼動作的函數。
首先要為該 Reducer 設計它所管理的 state 架構:
const initState = {
name: 'Jack',
}
雖然在 Reducer 中也可以改變 state 的架構,但在 initState 中將資料架構清楚列出,還能讓接手的人或是兩個禮拜後的自己,一看就能知道這個 Reducer 保管了哪些資料。
現在有了初始資料,就可以使用它建立一個 Reducer :
const reducer = (state = initState, action) => {
switch (action.type){
default:
return state
}
}
每一個 Reducer 都會有兩個參數,第一個參數會將初始的資料狀態 initState 交由 Reducer 保管,第二個參數會傳入現在 reducer 要對 state 做什麼動作的指令及額外的參數,這些在後幾篇會再講解,所以在還沒有任何 action 指令描述的 Reducer 內,預設回傳了它所保管的 state ,在這裡就是上方的 initState 。
創建 store
創建 Reducer 後,還得將它交由 store , store 的工作就是在應用程式中負責整合所有的 Reducer 。
創建前,得先從 redux 中 import 進負責創建 store 的函式 createStore ,並將 Reducer 傳入以創建一個 store:
import { createStore } from 'redux'
const store = createStore(reducer)
//可使用 store 的內建函式 getState() 確認目前 store 內所保管的資料console.log(store.getState()) // {name: 'Jack'}
需要注意的是,每個專案都應該只能有一個 store 存在,若是有許多不同類型的資料,則是以 Reducer 區分,最後將多個 Reducer 打包成一個後,再創建 store ,這部分的使用方法,在日後的文章也會解說。
store 產生後, Redux 的前置準備部分就告一段落,接下來說明 React 的 component 該如何從 Redux 的 store 中取到資料。
useSelector
這個 Hooks 的作用就是從 Store 中,將 Component 需要的 State 取出,如果有使用過 Redux Saga ,它的作用就很像 select ,只要透過 useSelector 什麼資料都輕易到手:
import { useSelector } from 'react-redux'
function Greeting() {
// selector function:拿到整個 state,回傳你要的那一塊
const name = useSelector((state) => state.name)
return <h1>Hello, {name}</h1>
}
useSelector 會幫你訂閱 store,當你選取的那塊 state 改變時,component 會自動 re-render。要注意 selector 預設用 === 比較回傳值,所以不要在 selector 裡回傳新物件 / 新陣列(每次都是新 reference 會造成多餘的 re-render),需要回傳衍生資料時搭配 reselect 或淺比較。
Reducer & Store
Reducer 仍然是「(state, action) => newState」的純函式,職責沒變;改變的是創建 store 的方式。早期用 redux 的 createStore:
import { createStore } from 'redux'
const store = createStore(reducer)
但 createStore 在現代已經標示為 deprecated(呼叫時 TS / IDE 會劃刪除線),官方改推 Redux Toolkit(RTK) 的 configureStore。RTK 把樣板程式碼包掉,預設幫你裝好 Redux DevTools、redux-thunk、以及開發環境的不可變 / 序列化檢查。
搭配 createSlice 寫一個簡單的待辦事項,Reducer 很單純:
import { createSlice, configureStore } from '@reduxjs/toolkit'
const todosSlice = createSlice({
name: 'todos',
initialState: [] as string[],
reducers: {
// RTK 內建 Immer,這裡看起來像「直接 mutate」其實是產生新 state
addTodo: (state, action) => {
state.push(action.payload)
},
clearTodos: () => [],
},
})
export const { addTodo, clearTodos } = todosSlice.actions
const store = configureStore({
reducer: {
todos: todosSlice.reducer,
},
})
createSlice 會依 reducers 裡的 key 自動生成對應的 action creator(上面的 addTodo / clearTodos),不用再手寫 action.type 字串常數跟 switch。configureStore 的 reducer 欄位接一個物件,等同於以前的 combineReducers,多個 slice 直接掛上去即可。