一起來了解React的Hook
State Hook
這個例子用來顯示一個計(jì)數(shù)器。當(dāng)你點(diǎn)擊按鈕,計(jì)數(shù)器的值就會增加:
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
就是一個 Hook (等下我們會講到這是什么意思)。通過在函數(shù)組件里調(diào)用它來給組件添加一些內(nèi)部 state
。React 會在重復(fù)渲染時保留這個 state
。useState
會返回一對值:當(dāng)前狀態(tài)和一個讓你更新它的函數(shù),你可以在事件處理函數(shù)中或其他一些地方調(diào)用這個函數(shù)。它類似 class
組件的 this.setState
,但是它不會把新的 state
和舊的 state
進(jìn)行合并。(我們會在使用 State Hook 里展示一個對比 useState
和 this.state
的例子)。
useState
唯一的參數(shù)就是初始 state
。在上面的例子中,我們的計(jì)數(shù)器是從零開始的,所以初始 state
就是 0
。值得注意的是,不同于 this.state
,這里的 state
不一定要是一個對象 —— 如果你有需要,它也可以是。這個初始 state
參數(shù)只有在第一次渲染時會被用到。
聲明多個 state 變量
你可以在一個組件中多次使用 State Hook:
function ExampleWithManyStates() { // 聲明多個 state 變量! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... }
數(shù)組解構(gòu)的語法讓我們在調(diào)用 useState
時可以給 state
變量取不同的名字。當(dāng)然,這些名字并不是 useState
API 的一部分。React 假設(shè)當(dāng)你多次調(diào)用 useState
的時候,你能保證每次渲染時它們的調(diào)用順序是不變的。后面我們會再次解釋它是如何工作的以及在什么場景下使用。
那么,什么是 Hook?
Hook 是一些可以讓你在函數(shù)組件里 " 鉤入" React state
及生命周期等特性的函數(shù)。Hook 不能在 class
組件中使用 —— 這使得你不使用 class
也能使用 React。(我們不推薦把你已有的組件全部重寫,但是你可以在新組件里開始使用 Hook。)
React 內(nèi)置了一些像 useState
這樣的 Hook。你也可以創(chuàng)建你自己的 Hook 來復(fù)用不同組件之間的狀態(tài)邏輯。我們會先介紹這些內(nèi)置的 Hook。
Effect Hook
你之前可能已經(jīng)在 React 組件中執(zhí)行過數(shù)據(jù)獲取、訂閱或者手動修改過 DOM。我們統(tǒng)一把這些操作稱為“副作用”,或者簡稱為“作用”。
useEffect
就是一個 Effect Hook,給函數(shù)組件增加了操作副作用的能力。它跟 class
組件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的用途,只不過被合并成了一個 API。
例如,下面這個組件在 React 更新 DOM 后會設(shè)置一個頁面標(biāo)題:
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // 相當(dāng)于 componentDidMount 和 componentDidUpdate: useEffect(() => { // 使用瀏覽器的 API 更新頁面標(biāo)題 document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
當(dāng)你調(diào)用 useEffect
時,就是在告訴 React 在完成對 DOM 的更改后運(yùn)行你的“副作用”函數(shù)。由于副作用函數(shù)是在組件內(nèi)聲明的,所以它們可以訪問到組件的 props
和 state
。默認(rèn)情況下,React 會在每次渲染后調(diào)用副作用函數(shù) —— 包括第一次渲染的時候。
副作用函數(shù)還可以通過返回一個函數(shù)來指定如何“清除”副作用。例如,在下面的組件中使用副作用函數(shù)來訂閱好友的在線狀態(tài),并通過取消訂閱來進(jìn)行清除操作:
import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
在這個示例中,React 會在組件銷毀時取消對 ChatAPI
的訂閱,然后在后續(xù)渲染時重新執(zhí)行副作用函數(shù)。(如果傳給 ChatAPI
的 props.friend.id
沒有變化,你也可以告訴 React 跳過重新訂閱。)
跟 useState
一樣,你可以在組件中多次使用 useEffect
:
function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ...
通過使用 Hook,你可以把組件內(nèi)相關(guān)的副作用組織在一起(例如創(chuàng)建訂閱及取消訂閱),而不要把它們拆分到不同的生命周期函數(shù)里。
Hook 使用規(guī)則
Hook 就是 JavaScript 函數(shù),但是使用它們會有兩個額外的規(guī)則:
- 只能在函數(shù)最外層調(diào)用 Hook。不要在循環(huán)、條件判斷或者子函數(shù)中調(diào)用。
- 只能在 React 的函數(shù)組件中調(diào)用 Hook。不要在其他 JavaScript 函數(shù)中調(diào)用。(還有一個地方可以調(diào)用 Hook —— 就是自定義的 Hook 中,我們稍后會學(xué)習(xí)到。)
自定義 Hook
有時候我們會想要在組件之間重用一些狀態(tài)邏輯。目前為止,有兩種主流方案來解決這個問題:高階組件和 render props
。自定義 Hook 可以讓你在不增加組件的情況下達(dá)到同樣的目的。
前面,我們介紹了一個叫 FriendStatus
的組件,它通過調(diào)用 useState
和 useEffect
的 Hook 來訂閱一個好友的在線狀態(tài)。假設(shè)我們想在另一個組件里重用這個訂閱邏輯。
首先,我們把這個邏輯抽取到一個叫做 useFriendStatus
的自定義 Hook 里:
import React, { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; }
它將 friendID
作為參數(shù),并返回該好友是否在線:
現(xiàn)在我們可以在兩個組件中使用它:
function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); }
每個組件間的 state
是完全獨(dú)立的。Hook 是一種復(fù)用狀態(tài)邏輯的方式,它不復(fù)用 state
本身。事實(shí)上 Hook 的每次調(diào)用都有一個完全獨(dú)立的 state
—— 因此你可以在單個組件中多次調(diào)用同一個自定義 Hook。
自定義 Hook 更像是一種約定而不是功能。如果函數(shù)的名字以 use
開頭并調(diào)用其他 Hook,我們就說這是一個自定義 Hook。 useSomething
的命名約定可以讓我們的 linter
插件在使用 Hook 的代碼中找到 bug。
你可以創(chuàng)建涵蓋各種場景的自定義 Hook,如表單處理、動畫、訂閱聲明、計(jì)時器,甚至可能還有更多我們沒想到的場景。我們很期待看到 React 社區(qū)會出現(xiàn)什么樣的自定義 Hook。
其他 Hook
除此之外,還有一些使用頻率較低的但是很有用的 Hook。比如,useContext
讓你不使用組件嵌套就可以訂閱 React 的 Context。
function Example() { const locale = useContext(LocaleContext); const theme = useContext(ThemeContext); // ... }
另外 useReducer
可以讓你通過 reducer
來管理組件本地的復(fù)雜 state
。
function Todos() { const [todos, dispatch] = useReducer(todosReducer); // ...
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
react hook使用useState更新數(shù)組,無法更新問題及解決
這篇文章主要介紹了react hook使用useState更新數(shù)組,無法更新問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03redux持久化之redux-persist結(jié)合immutable使用問題
這篇文章主要為大家介紹了redux持久化之redux-persist結(jié)合immutable使用問題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08ReactJS?應(yīng)用兼容ios9對標(biāo)ie11解決方案
這篇文章主要為大家介紹了ReactJS?應(yīng)用兼容ios9對標(biāo)ie11解決方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01react-router-dom?v6?使用詳細(xì)示例
這篇文章主要介紹了react-router-dom?v6使用詳細(xì)示例,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助2022-09-09在?React?Native?中給第三方庫打補(bǔ)丁的過程解析
這篇文章主要介紹了在?React?Native?中給第三方庫打補(bǔ)丁的過程解析,有時使用了某個React Native 第三方庫,可是它有些問題,我們不得不修改它的源碼,本文介紹如何修改源碼又不會意外丟失修改結(jié)果的方法,需要的朋友可以參考下2022-08-08