要點(diǎn):
- 可以在不編寫(xiě)class的情況下使用 state 以及其他react特性。
- Hook沒(méi)有破壞性改動(dòng)碱工, 完全是可選的庵寞,且向后兼容茅姜。
- 主要是為了解決邏輯復(fù)用的問(wèn)題闪朱。
- 鼓勵(lì)開(kāi)發(fā)者將業(yè)務(wù)通用邏輯封裝成React Hook 而不是工具函數(shù)。
動(dòng)機(jī):
- 在hook出現(xiàn)之前钻洒,React 并沒(méi)有提供一種很好的將可復(fù)用行為‘附加’到組件的途徑(比如連接store),如果想復(fù)用邏輯奋姿,只能通過(guò)render props 或HOC 的形式,但是這樣會(huì)讓組件的結(jié)構(gòu)變得復(fù)雜素标,同時(shí)組件嵌套也太多称诗,造成‘嵌套地獄’(比如很多的Provider 和Consumer)
Hook是什么?
- Hook實(shí)際上是一些函數(shù)头遭,這些函數(shù)與React生成的Fiber節(jié)點(diǎn)相關(guān)聯(lián)寓免。
- Hook實(shí)際上是利用了閉包,用閉包保存變量计维,并且在合適的時(shí)機(jī)觸發(fā)fiber更新或者執(zhí)行副作用袜香。
- Hook依賴(lài)于React特定的上下文,在React之外使用Hook 不會(huì)有任何效果鲫惶。
使用Hooks的好處:
- 如果業(yè)務(wù)邏輯變更蜈首,需要在函數(shù)式組件中使用生命周期和狀態(tài), 就不需要再把函數(shù)式組件轉(zhuǎn)化為類(lèi)組件欠母,擴(kuò)大函數(shù)組件的應(yīng)用范圍欢策。
- 易于提取可復(fù)用邏輯,是組件更簡(jiǎn)單艺蝴。
Hook使用注意點(diǎn):
- 不要在條件語(yǔ)句中使用
- 不要在循環(huán)中使用
- useEffect 接受的函數(shù)返回值只能是函數(shù)或者undefined.
常用hook
- useState()
- 在組件中保存變量猬腰,在設(shè)置變量時(shí)會(huì)觸發(fā)組件的更新(如果有多個(gè)變量,是否會(huì)觸發(fā)多次更新)猜敢。
- 接收的參數(shù)可以是值或者函數(shù),如果是函數(shù)盒延,在組件掛載時(shí)執(zhí)行
- setState會(huì)將這一次的變量和上一次的變量進(jìn)行比較缩擂,如果是同一個(gè),便會(huì)跳過(guò)此次執(zhí)行添寺, 不會(huì)觸發(fā)更新胯盯。
- useEffect()
- 在react的生命周期里執(zhí)行一些副作用(除了狀態(tài)相關(guān)的邏輯,比如網(wǎng)絡(luò)請(qǐng)求计露,監(jiān)聽(tīng)事件博脑,查找 dom)憎乙,接收兩個(gè)參數(shù),第一個(gè)參數(shù)是函數(shù)叉趣,第二個(gè)參數(shù)為依賴(lài)數(shù)組泞边,數(shù)組里的值發(fā)生變化便會(huì)觸發(fā)useEffect,如果數(shù)組為空,默認(rèn)是在組件的掛載和銷(xiāo)毀時(shí)執(zhí)行疗杉。第一個(gè)參數(shù)可返回一個(gè)函數(shù)阵谚,在組件卸載時(shí)執(zhí)行。
useContext()
useMemo()
const handleClick = useMemo(() => {},[])
- 同useCallback()用法烟具。
- useMemo()可返回函數(shù)或者返回值梢什,處理計(jì)算結(jié)果的的緩存或引入組件,防止重復(fù)掛載朝聋。
- 第二個(gè)參數(shù):不傳數(shù)組嗡午,每次更新都會(huì)重新計(jì)算;傳入空數(shù)組冀痕,只會(huì)計(jì)算一次翼馆;傳入依賴(lài)對(duì)應(yīng)的值,當(dāng)對(duì)應(yīng)的值發(fā)生變化時(shí)金度,才會(huì)重新計(jì)算(可以依賴(lài)另外一個(gè) useMemo 返回的值)
// useMemo示例
function Child({ count }){
return <p>當(dāng)前傳遞的count為:{count}</p>
}
export default function HookDemo {
const [count1, setCount1] = useState(0)
const [count2, setCount2] = useState(10)
const child = useMemo(() => {
message.info('重新生成child組件')
return <Child count={count1} />
},[count1])
return <div>{child}</div>
}
- useCallback()
const handleClick = useCallback(() => {callback},[])
- 存儲(chǔ)參數(shù)中使用的回調(diào)应媚,當(dāng)依賴(lài)數(shù)組發(fā)生變化時(shí),該回調(diào)才會(huì)重新創(chuàng)建猜极。
- 可以說(shuō)是 useMemo 的語(yǔ)法糖中姜,能用 useCallback 實(shí)現(xiàn)的,都可以使用 useMemo, 在 react 中我們經(jīng)常面臨一個(gè)子組件渲染優(yōu)化的問(wèn)題跟伏,尤其是在向子組件傳遞函數(shù)props時(shí)丢胚,每次 render 都會(huì)創(chuàng)建新函數(shù),導(dǎo)致子組件不必要的渲染受扳,浪費(fèi)性能携龟,這個(gè)時(shí)候,就是 useCallback 的用武之地了勘高,useCallback 可以保證峡蟋,無(wú)論 render 多少次,我們的函數(shù)都是同一個(gè)函數(shù)华望,減小不斷創(chuàng)建的開(kāi)銷(xiāo)
- useRef()
- 一般用取dom引用和組件的值蕊蝗。
- 在函數(shù)組件中的一個(gè)全局變量,不會(huì)因?yàn)橹貜?fù) render 重復(fù)申明赖舟, 類(lèi)似于類(lèi)組件的 this.xxx
- 返回一個(gè)可變的ref對(duì)象蓬戚,其.current屬性被初始化為傳入的參數(shù)(initialValue)。返回的ref對(duì)象在組件的整個(gè)生命周期內(nèi)保持不變宾抓。
forwardRef: 應(yīng)用父組件的ref實(shí)例子漩,成為子組件的一個(gè)參數(shù)豫喧,可以引用父組件的ref綁定到子組件自身的節(jié)點(diǎn)上。
- useImperativeHandle()
- 第一個(gè)參數(shù)接收一個(gè)通過(guò) forwardRef 引用父組件的ref實(shí)例幢泼,第二個(gè)參數(shù)為回調(diào)函數(shù)紧显,返回一個(gè)對(duì)象,對(duì)象里面存儲(chǔ)需要(按需)暴露給父組件的屬性或方法旭绒。
官方建議useImperativeHandle和forwardRef同時(shí)使用鸟妙,減少暴露給父組件的屬性,避免使用ref這樣的命令式代碼挥吵。
舉例:
function Chindren (props, ref) {
const childRef = useRef()
const introduce = useCallback (() => {
console.log('this is my introduction')
}, [])
useImperativeHandle(ref, () => ({
introduce: () => {
introduce()
}
}));
return (
<div ref={childRef }> { props.count }</div>
)
}
const ChildrenComp= forwardRef(Chindren )
function App () {
const [ count, setCount ] = useState(0)
const parentRef = useRef(null)
const onClick = useCallback (() => {
setCount(count => count + 1)
parentRef.current.introduce()
}, [])
return (
<div>
點(diǎn)擊次數(shù): { count }
<ChildrenComp ref={parentRef } count={count}></ChildrenComp>
<button onClick={onClick}>點(diǎn)我</button>
</div>
)
}
8.自定義Hook
- 基本使用:
// 編寫(xiě)自己的Hook
function useCounter(initialValue){
const [count, changeCount] = useState(initialValue)
// 減少
const decrease = () => { changeCount(count - 1) }
// 增加
const increase = () => { changeCount(count + 1) }
// 重置
const resetCounter = () => { changeCount(0)}
return [count, {decrease, increase, resetCounter}]
}
export default function myHookView () {
const [count, controlCount] = useCounter(10)
return (
<div>
<Button onClick={ controlCount.decrease }>減少</button>
</div>
)
}
- 返回DOM的鉤子
// modal框示例
function useModal(){
const [visible, setVisible] = useState(false)
const toggleModalVisible = () => { changeVisible(!visible)}
return [
(<Modal visible={visible} onOk={toggleModalVisible(visible)} onCancel={toggleModalVisible}>彈窗內(nèi)容</Modal>), toggleModalVisible]
}
export default function HookDemo () {
const [modal, toggleModal] = useModal()
return (
<div>
{modal}
<Button onClick={toggleModal}>打開(kāi)彈窗</button>
</div>
)
}