6-5 建立自己的鉤子 - Custom Hooks
本單元對應的專案分支為:custom-hooks。
#
單元核心這個單元的主要目標包含:
- 了解如何將重複的邏輯整理成自己可使用的 Hooks
延續上一個單元專案程式碼的重構,現在重構後的 App.js
中,雖然已經比起原本的程式碼乾淨許多,基本上只做了拉取資料的動作,但因為 fetchCurrentWeather
和 fetchWeatherForecast
本身也做了不少事情,取得資料之後又要透過 setWeatherElement
把資料存到 React 元件中,我們有沒有什麼方式讓這個元件再更乾淨一些呢?
答案是肯定的,在 React 中,我們不只能夠使用 React 預先定義好的 Hooks,像是之前使用的 useState
、useEffect
、useMemo
這些,還可以自己自訂 Hook。自訂的 Hook 可以幫我們把較複雜的程式邏輯抽到 Hook 內,並且可以在多個元件內重複使用外,甚至也可以打包起來,放到開源社群分享給有同樣需求的人使用。
現在就讓我們來看看怎麼樣定義自己的 Hook 吧!
#
Custom Hook 的概念自訂 Hook(Custom Hook)的概念其實很簡單,它和你之前寫的 React Component 基本上是一樣的,都是 JavaScript 的函式,而且在 Custom Hook 中一樣可以使用 useState
、useEffect
這些原本 React 就有提供的 Hooks,只是在 React Component 中最後你會回傳的是 JSX,而在 Hook 中最後回傳的是一些資料或改變資料的方法。此外,在自訂的 Hook 中,會遵循 React Hooks 的慣例,因此會使用 use
開頭來為該函式命名。
所以基本上你會建立 React Component 的話,就會自訂 Hook。另外,自訂的 Hook 一樣要遵守原本 React Hooks 的原則,像是 Hook 只能在 React 的 Functional Component 中使用(過去 React Component 除了函式之外,也可以用 class
建立)、Hook 不能放在迴圈或 if
判斷式內等等。
#
新增 useWeatherAPI 的 Hook現在就讓我們來建立一個名為 useWeatherAPI
的 Hook,在這個 Hook 中會幫助我們去向中央氣象局發送 API 請求,並且回傳取得的資料。
先在 ./src
資料夾中建立一個名為 hooks
資料夾,並新增一支名為 useWeatherAPI.js
的檔案,在裡面定義一個名為 useWeatherAPI
的函式,並透過 export
匯出:
其實和建立 React Component 的步驟一樣吧!
#
定義 Custom Hook 內的功能接下來在 useWeatherAPI
這個函式中,就可以來向中央氣象局發送 API 請求天氣資料,這個部分因為先前都寫在 App.js
中了,因此把這個部分剪下貼上就好。
先把在 App.js
中定義的 fetchCurrentWeather
、fetchWeatherForecast
這兩個函式剪下,貼到 useWeatherAPI.js
中:
接著把原本寫在 App
元件中和拉取天氣資料有關的部分,搬到這個 useWeatherAPI
這個 Hook 內,其中包含:
- 匯入 react 套件提供的
useState
,useEffect
,useCallback
方法。在 Custom Hooks 中因為最後不會回傳 JSX,因此不需要匯入 react 套件提供的 React 物件:
useState
中用來定義weatherElement
的部分
- 透過
useCallback
用來定義fetchData()
的部分
- 透過
useEffect
用來呼叫fetchData
的部分
- 最後一個步驟是和一般 React 元件最不同的地方,一般的 React 元件最終通常都是回傳 JSX,但在 Custom Hooks 中最後會
return
的是可以讓其他 React 元件使用的資料或方法。這就像當我們呼叫useState
是會得到一個資料狀態和用來改變資料狀態的方法。所以這裡我們會回傳用來拉取資料的方法(fetchData
)和拉取資料後取得的天氣資料(weatherElement
)
現在我們就定義好了 useWeatherAPI
這個 Custom Hook。
#
讓 Custom Hook 可以接收參數Custom Hook 本質上也是個函式,所以它也可透過參數的方式取得資料。現在在 useWeatherAPI
中,我們還缺少幾個資料,像是 AUTHORIZATION_KEY
、LOCATION_NAME
和 LOCATION_NAME_FORECAST
,這些資料原本是放在 App 元件中,為了要讓 useWeatherAPI
也能取得這些資料,可以透過函式的參數把資料帶進來:
這裡可以看到用來取得即時天氣的 API(fetchCurrentWeather
)需要帶入的是地區(即「臺北」);用來取得天氣預報的 API(fetchWeatherForecast
)需要帶入的是縣市名稱(即「臺北市」),之所以會有這樣的差別,主要是因為中央氣象局在這兩道 API 需要的資料不同。為了讓變數的語意更清楚,我們把 LOCATION_NAME_FORECAST
改名為 cityName
。
現在進一步把這個變數帶入 fetchCurrentWeather
和 fetchWeatherForecast
中:
接著在 useWeatherAPI 的函式中,一樣透過參數把資料帶入這兩個方法:
- 把
authorizationKey
,locationName
,cityName
傳到拉取 API 的方法中 - 在 useCallback 中要記得把變數放入 dependencies array 中,以確保這些資料改變時,能夠得到最新的 fetchData 方法
#
使用 Custom Hook當我們把拉取天氣資料的這一整個流程包成 Custom Hook 之後,在需要使用到天氣資料的 React 元件中,都可以透過它就可以取得中央氣象局回傳的資料。
使用方式非常簡單,就和使用其他的 React Hooks 一樣,現在就讓我們在 App.js
中來使用 useWeatherAPI
:
- 透過
import
載入useWeatherAPI
這個 Custom Hook - 直接呼叫
useWeatherAPI
後就能取得該 Hook 回傳的weatherElement
和fetchData
方法 - 在呼叫
useWeatherAPI()
中,把它所需的參數locationName
,cityName
,authorizationKey
放進去 - 整個使用方式是不是就和
useState
非常類似呢?
當啷~畫面又回來拉~
基本上 Custom Hook 的定義和使用都不難,如果你會撰寫 React 中的 Functional Component,就一定會撰寫 Custom Hook。透過 Custom Hook ,可以幫助開發者將具有相同邏輯的功能統整在一個 Hook 中,方便重複使用這個函式的功用!
在這兩個單元中一口氣重構了不少程式碼,目的都是為了讓整個專案後續更容易維護,至於要怎麼判斷好不好維護,最簡單的方式是:「不求別人接手看得懂,只求自己一個月後打開程式還改得動」,如果現在寫的東西未來一個月後自己都看不懂的話,那肯定是不太好維護。
#
換你了!建立自己的 HookCustom Hooks 在撰寫時要寫的是這個 Hook 帶入什麼樣的 input 後,將可以得到什麼樣的 output,與一般函式的思路蠻相近的。
現在要請你將與中央氣象局拉取資料有關的 API 拆分成一個獨立的 React Hook,想要使用這個 Hook 的人,只需要帶入 locationName
, cityName
, authorizationKey
之後,就可以取得對應的天氣資料。由於重構時程式碼的變動可能比較複雜,需要的時候都可以對應最下方的連結查看。實作時可以參考以下步驟:
- 在
./src
資料夾中,建立名為hooks
的資料夾,並在裡面新增useWeatherAPI.js
,在檔案中建立一個名為useWeatherAPI
的函式 - 把在
App.js
中定義的fetchCurrentWeather
、fetchWeatherForecast
這兩個函式剪下,貼到useWeatherAPI.js
中 - 在
useWeatherAPI.js
中匯入 react 套件提供的useState
,useEffect
,useCallback
方法 - 把原本寫在
App
元件中和拉取天氣資料有關的部分可以搬到這個useWeatherAPI
這個 Hook 內,其中包含useState
中用來定義weatherElement
的部分、透過useCallback
用來定義fetchData()
的部分、透過useEffect
用來呼叫fetchData
的部分 - 把需要在 App 元件中使用到的函式和方法於 useWeatherAPI 回傳
- 讓 useWeatherAPI 可以接收參數(
locationName
,cityName
,authorizationKey
) - 在 App 元件中使用 useWeatherAPI 這個 Custom Hook
本單元相關之網頁連結、完整程式碼與程式碼變更部分可於 custom-hooks
分支檢視:https://github.com/pjchender/learn-react-from-hook-realtime-weather-app/tree/custom-hooks