系列文章傳送門:
react 進階必學 hook (一):useState 來一碗大碗寬面
react 進階必學 hook (二):useEffect 專治不吃寬面
react 進階必學 hook (三):useContext 麪館分店開張了
什麼是自定義hook
官網原話如下:
自定義 Hook 是一個函數,其名稱以 “
use
” 開頭,函數內部可以調用其他的 Hook,自定義 Hook 是一種自然遵循 Hook 設計的約定,而並不是 React 的特性
自定義hook有一下特性或是約定:
- 自定義hook中可以調用其他hook
- 必須以use開頭,就像組件必須以大寫字母開頭一樣
- 自定義hook中管理state也是使用useState、useEffect,因爲useState在調用的時候就是完全獨立的
自定義hook解決了什麼問題
想必開發人員對utils文件夾都很熟悉,在開發的時候我們會把經常使用的一些方法函數放到utils文件夾中,因爲這些邏輯方法的複用率很高;同樣自定義hook也是解決類似的問題。
React的組件思想其實就是一種將代碼複用的做法,然而在編寫組件的過程中又有一些邏輯是可以複用的,但是這些邏輯不需要在ui上展示或是有些組件不能在更細的拆分,因此我們可以將這些邏輯抽象成自定義hook,和組件的抽象不是一個緯度,就像抽象工廠模式;這種功能的抽象,嚴格控制輸入輸出,還有點函數式編程的思想,自定義的hook我們可以在不同的組件中使用,就像utils中的方法一樣。
麪館例子
react 進階必學 hook (三):useContext 麪館分店開張了中我們添加了一個統計一共賣出多少面的需求,每一個麪館賣出一碗麪後都做了兩個動作:
- 自己麪館的
counter
顯式加一 - 麪館總計的
counter
顯式加一
代碼如下:
const [count, setCount] = useState(0);
const {counter, add} = useContext(context)
useEffect(() => {
add()
return window.alert(`一共賣出${counter}碗💪💪💪💪`)
}, [count])
其中 const [count, setCount] = useState(0);
是本麪館的counter,add()
控制的是上下文中的總計的counter,兩個是分開的,但是仔細想一下,這兩個過程其實是嚴格綁定的,如果添加其他面的品類,添加新的邏輯就會分散導致不好維護,因此可以將這個邏輯寫成一個hook。
1. 創建自定義hook
我們先創建一個自定義hook,命名爲useCounter
,因爲要求必須以use開頭:
export default function useCounter(initialState) {
const [count, setCount] = useState(initialState)
const {counter, add} = useContext(context)
const setCounter = ()=>{
setCount(count+1) // 本麪館狀態更改
add() // 總計更改
}
useEffect(() => {
window.alert(`一共賣出${counter}碗💪💪💪💪`)
}, [count])
return [count, setCounter]
}
對於自定義hook的返回return [count, setCounter]
中使用[]
還是{}
,要分情況處理,使用時需要更換變量名的話建議用[]
2. 修改麪館邏輯,使用自定義hook
想useState一樣使用即可:
const [count, setCount] = useCounter(0);
完整代碼如下:
import React, {useState} from 'react';
import useCounter from './useCounter'
export default function Counter(props) {
const [count, setCount] = useCounter(0); // 使用自定義hook
const [big, setSize] = useState(true);
const [type, setType] = useState("寬面");
const size = (big)=>big? "大":"小";
return (
<div>
<h3>麪館{props.shop}</h3>
<h2>老闆,來{count}碗{size(big)}碗{type}</h2>
<button onClick={()=> setCount(count+1)} style={{"height":"40px"}}>加一碗</button>
<button onClick={()=> setSize(!big)} style={{"height":"40px"}}>{size(!big)}碗</button>
<button onClick={()=> setType("細面")} style={{"height":"40px"}}>不想吃寬面</button>
</div>
);
}
對比一下之前的代碼,其實組件內部也乾淨了不少:
![Peek 2020-05-31 19-29](/home/ffzs/視頻/Peek 2020-05-31 19-29.gifexport default function Counter(props) {
const [count, setCount] = useState(0);
const [big, setSize] = useState(true);
const [type, setType] = useState("寬面");
const size = (big)=>big? "大":"小";
const {counter, add} = useContext(context)
useEffect(() => {
add()
return window.alert(`一共賣出${counter}碗💪💪💪💪`)
}, [count])
return (
<div>
<h3>麪館{props.shop}</h3>
<h2>老闆,來{count}碗{size(big)}碗{type}</h2>
<button onClick={()=> setCount(count+1)} style={{"height":"40px"}}>加一碗</button>
<button onClick={()=> setSize(!big)} style={{"height":"40px"}}>{size(!big)}碗</button>
<button onClick={()=> setType("細面")} style={{"height":"40px"}}>不想吃寬面</button>
</div>
);
}
3. 效果展示
功能跟之前完全相同。