一、是什麼
Hook
是 React 16.8 的新增特性。它可以讓你在不編寫 class
的情況下使用 state
以及其他的 React
特性
至於為什麼引入hook
,官方給出的動機是解決長時間使用和維護react
過程中常遇到的問題,例如:
- 難以重用和共享元件中的與狀態相關的邏輯
- 邏輯複雜的元件難以開發與維護,當我們的元件需要處理多個互不相關的 local state 時,每個生命週期函式中可能會包含著各種互不相關的邏輯在裡面
- 類元件中的this增加學習成本,類元件在基於現有工具的優化上存在些許問題
- 由於業務變動,函式元件不得不改為類元件等等
在以前,函式元件也被稱為無狀態的元件,只負責渲染的一些工作
因此,現在的函式元件也可以是有狀態的元件,內部也可以維護自身的狀態以及做一些邏輯方面的處理
二、有哪些
上面講到,Hooks
讓我們的函式元件擁有了類元件的特性,例如元件內的狀態、生命週期
最常見的hooks
有如下:
- useState
- useEffect
- 其他
useState
首先給出一個例子,如下:
import React, { useState } from 'react'; function Example() { // 宣告一個叫 "count" 的 state 變數 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
在函式元件中通過useState
實現函式內部維護state
,引數為state
預設的值,返回值是一個陣列,第一個值為當前的state
,第二個值為更新state
的函式
該函式元件等價於的類元件如下:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
從上述兩種程式碼分析,可以看出兩者區別:
-
state宣告方式:在函式元件中通過 useState 直接獲取,類元件通過constructor 建構函式中設定
-
state讀取方式:在函式元件中直接使用變數,類元件通過
this.state.count
的方式獲取 -
state更新方式:在函式元件中通過 setCount 更新,類元件通過this.setState()
總的來講,useState 使用起來更為簡潔,減少了this
指向不明確的情況
useEffect
useEffect
可以讓我們在函式元件中進行一些帶有副作用的操作
同樣給出一個計時器示例:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } componentDidMount() { document.title = `You clicked ${this.state.count} times`; } componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
從上面可以看見,元件在載入和更新階段都執行同樣操作
而如果使用useEffect
後,則能夠將相同的邏輯抽離出來,這是類元件不具備的方法
對應的useEffect
示例如下:
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
useEffect
第一個引數接受一個回撥函式,預設情況下,useEffect
會在第一次渲染和更新之後都會執行,相當於在componentDidMount
和componentDidUpdate
兩個生命週期函式中執行回撥
如果某些特定值在兩次重渲染之間沒有發生變化,你可以跳過對 effect 的呼叫,這時候只需要傳入第二個引數,如下:
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // 僅在 count 更改時更新
上述傳入第二個引數後,如果 count
的值是 5
,而且我們的元件重渲染的時候 count
還是等於 5
,React 將對前一次渲染的 [5]
和後一次渲染的 [5]
進行比較,如果是相等則跳過effects
執行
回撥函式中可以返回一個清除函式,這是effect
可選的清除機制,相當於類元件中componentwillUnmount
生命週期函式,可做一些清除副作用的操作,如下:
useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; });
所以, useEffect
相當於componentDidMount
,componentDidUpdate
和 componentWillUnmount
這三個生命週期函式的組合
其它 hooks
在元件通訊過程中可以使用useContext
,refs
學習中我們也用到了useRef
獲取DOM
結構......
還有很多額外的hooks
,如:
- useReducer
- useCallback
- useMemo
- useRef
三、解決什麼
通過對上面的初步認識,可以看到hooks
能夠更容易解決狀態相關的重用的問題:
-
每呼叫useHook一次都會生成一份獨立的狀態
-
通過自定義hook能夠更好的封裝我們的功能
編寫hooks
為函數語言程式設計,每個功能都包裹在函式中,整體風格更清爽,更優雅
hooks
的出現,使函式元件的功能得到了擴充,擁有了類元件相似的功能,在我們日常使用中,使用hooks
能夠解決大多數問題,並且還擁有程式碼複用機制,因此優先考慮hooks