回來填坑。
簡述
之前碰到這個 hook 時就想說等學完 Redux 再來詳細解說吧,結果一眨眼兩、三個月就過去了 XD,總之這篇要來解釋一下 useReducer 的用途。
我覺得當這個 hook 很適合用在「當你不需要用到 Redux,但又想要全域 state」的時候。可以利用它來跟 Context 結合出一個替代方案。
所以看這篇前最好先知道一下 useContext 在幹嘛?因為等一下的範例會用到,建議可以先去看我之前寫的這兩篇:
useReducer 的好用之處?
「在學任何東西前,先問一下為什麼要用這個東西?」是句很經典的話,所以我們先來舉一個範例,一開始不使用 useReducer 接著再來解釋如果用了 useReducer 能帶來什麼好處?
假設我們目前要做一個可以切換「顏色主題」的網頁,底下的元件得根據目前的主題來切換樣式,所以除了需要 state 以外,我們還需要一個 Context 來避免「Props drilling」的問題。
所以呢,目前大概會有這樣的 code:
ThemeContext.jsx
1 | import { createContext } from 'react' |
這個是在 React-關於 useContext 更好的寫法 中提到的小技巧,忘記的話可以去複習一下。
App.js
1 | import Nav from "./Nav"; |
接著,假設我們想透過導覽列上的按鈕來切換顏色,可能就會寫出這樣的東西:
1 | import { useContext } from "react"; |
做到這邊有任何疑問的話可以先來這邊看一下範例,希望能幫助你理解一點。
總之呢,一個最簡單的 Context 就這樣完成了,但如果你仔細想一下會發現其實有可以改善的空間:
1. 邏輯全部塞在 function 裡
我知道這聽起來有點奇怪,畢竟在 function 放邏輯不是天經地義的事嗎?不然要放在哪裡?
別急,我的意思是指剛剛我們直接在 changeTheme 中用 setTheme 的這種方式其實應該有更好的寫法,畢竟當邏輯變得複雜時裡面的東西也會越來越多,可讀性就會沒那麼好。
這個時候如果我們可以透過 dispatch 的概念來改寫的話,不是會好很多嗎?像這樣:
1 | function changeTheme () { |
2. state / function 零散各地
意思是當 ThemeContext 想建立其他的 state 時,state 就會越來越多個,像這樣:
1 | import { createContext } from 'react' |
所以呢,這時候如果改用 useReducer 的話就可以改善這些問題。
加入 useReducer
這邊先介紹一下 useReducer 的用法,其實就跟在寫 redux 差不多,所以如果你對 redux 完全沒概念的話建議先去補一些相關知識再回來看,這邊不會解釋太深。
總之核心要素還是幾個東西:
- reducer
- action
- dispatch
首先 useReducer 會接收兩個參數,分別為 initialState 跟 reducer。
initialState 就是初始的 state,reducer 則是用來處理每個 action 的 function。
所以強調一下:
reducer就只是一個 functionreducer就只是一個 functionreducer就只是一個 function
接著 useReducer 會回傳兩個值(包在 Array 裡),分別為 state 跟 dispatch,前者就是 state 的值,後者則是用來發出 action 的一個 function。
其實這幾個東西都是 redux 的核心要素,所以再次建議你先去理解 redux 的概念後再回來看這篇,不然應該很難理解。
所以回到剛剛的例子,我們會把 useState 拿掉,改用 useReducer 來取代,變成這樣子:
1 | import { createContext, useReducer } from "react"; |
最後完成的 範例 在這邊,有什麼疑問的話可以去玩玩看。
回顧一下剛剛提到的兩個問題,這邊改用 useReducer 後其實就都解決了:
1. 邏輯都寫在 function 裡
現在 function 要做的事情很單純,就是 dispatch 一個 action 給 reducer,僅此而已。
2. 需要很多不同的 state 來管理不同狀態
當改用 useReducer 後,我們可以把所有 state 放在一個 Object 管理就好,不需要再拆成一個一個。
所以以上就是 useReducer 的範例,希望之後能運用這個輕量型的 hook 來實現簡單的狀態管理。