轉載於免費視頻網www.rails365.nethtml
構建本身的Hook
能夠將組件邏輯提取到可重用的函數中。react
當咱們學習使用`Effect Hook時, 咱們從聊天應用程序中看到了這個組件,該組件顯示一條消息,指示朋友是在線仍是離線:git
import { 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';
}
複製代碼
如今讓咱們的聊天應用程序也有一個聯繫人列表,咱們想要呈現綠色的在線用戶名。咱們能夠將上面相似的邏輯複製並粘貼到咱們的FriendListItem
組件中, 但它不是很好:github
import { useState, useEffect } from 'react';
function FriendListItem(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);
};
});
return (
<li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li>
);
}
複製代碼
相反,咱們想在FriendStatus
和FriendListItem
之間分享這個邏輯。函數
傳統上,在React
中,咱們有兩種流行的方式來共享組件之間的狀態邏輯:渲染道具和高階組件。咱們如今將看看Hook
如何在不添加這些方法的狀況下解 決許多相同的問題。學習
當咱們想要在兩個JavaScript
函數之間共享邏輯時,咱們將它提取到第三個函數。組件和掛鉤都是功能,因此這也適用於它們!測試
自定義Hook
其實也是一個JavaScript
函數,其名稱以use
開頭,能夠調用其餘Hook
。 例如,下面的useFriendStatus
是咱們的第一個自定義鉤子:動畫
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;
}
複製代碼
裏面沒有任何新內容 - 邏輯是從上面的組件中複製的。就像在組件中同樣,確保只在自定義Hook
的頂層無條件地調用其餘Hook
。spa
與React
組件不一樣,自定義Hook
不須要具備特定簽名。咱們能夠決定它做爲參數須要什麼,以及它應該返回什麼(若是有的話)。換句話說,它就像一個普通的功能。 它的名字應該始終使用use
開頭,這樣你就能夠一眼就看出鉤子的規則適用於它。.net
咱們使用FriendStatus Hook
的目的是訂閱咱們朋友的狀態。這就是爲何它將friendID
做爲參數,並返回此朋友是否在線:
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
return isOnline;
}
複製代碼
最初,咱們的目標是從FriendStatus
和FriendListItem
組件中刪除重複的邏輯。他們倆都想知道朋友是否在線。
如今咱們已經將這個邏輯提取到useFriendStatus鉤子,咱們可使用它:
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>
);
}
複製代碼
這段代碼是否等同於原始示例? 是的,他們以相同的方式工做。若是你仔細觀察,你會注意到咱們沒有對行爲作任何改變。咱們所作的只是將兩個函數之間的一些 公共代碼提取到一個單獨的函數中。自定義掛鉤是一種天然遵循Hooks
設計的約定,而不是React
功能。
我是否必須以use
開頭命名個人自定義Hook
? 咱們但願你作到這點。這個習慣很重要。若是沒有它,咱們將沒法自動檢查是否 違反了Hook規則,由於咱們沒法判斷某個函數是否包含對 其中的Hooks
的調用。
兩個組件使用相同的Hook
共享狀態嗎? 不會。自定義掛鉤是一種重用有狀態邏輯的機制(例如設置訂閱和記住當前值),但每次使用自定義掛鉤時, 自定義掛鉤的全部狀態和效果都是徹底獨立的。
自定義Hook
如何得到隔離狀態? 每次對Hook
的調用都會被隔離。由於咱們直接調用useFriendStatus
,從React
的角度來看, 咱們的組件只調用useState
和useEffect
。 正如咱們以前-所知,咱們能夠在一個組件中屢次調用useState
和useEffect
,它們將徹底獨立。
因爲Hooks
是函數,咱們能夠在它們之間傳遞信息。
爲了說明這一點,咱們將使用咱們假設的聊天示例中的另外一個組件。這是一個聊天消息收件人選擇器,顯示當前所選朋友是否在線:
const friendList = [
{ id: 1, name: 'Phoebe' },
{ id: 2, name: 'Rachel' },
{ id: 3, name: 'Ross' },
];
function ChatRecipientPicker() {
const [recipientID, setRecipientID] = useState(1);
const isRecipientOnline = useFriendStatus(recipientID);
return (
<>
<Circle color={isRecipientOnline ? 'green' : 'red'} />
<select
value={recipientID}
onChange={e : setRecipientID(Number(e.target.value))}
>
{friendList.map(friend : (
<option key={friend.id} value={friend.id}>
{friend.name}
</option>
))}
</select>
</>
);
}
複製代碼
咱們將當前選擇的friend ID
保留在recipientID
狀態變量中,若是用戶在<select>
選擇器中選擇其餘朋友,則更新它。
由於useState Hook
調用爲咱們提供了recipientID
狀態變量的最新值,因此咱們能夠將它做爲參數傳遞給咱們的自定義useFriendStatus Hook
:
const [recipientID, setRecipientID] = useState(1);
const isRecipientOnline = useFriendStatus(recipientID);
複製代碼
這讓咱們知道當前選擇的朋友是否在線。若是咱們選擇不一樣的朋友並更新recipientID
狀態變量,咱們的useFriendStatus Hook
將取消訂閱以前選擇的朋友, 並訂閱新選擇的朋友的狀態。
Custom Hooks
提供了之前在React
組件中沒法實現的共享邏輯的靈活性。您能夠編寫自定義Hook
,涵蓋普遍的用例,如表單處理,動畫,聲明訂閱,計時器, 以及可能還有更多咱們沒有考慮過的。更重要的是,能夠構建與React
的內置功能同樣易於使用的Hook
。
儘可能抵制過早添加抽象。既然功能組件能夠作得更多,那麼代碼庫中的平均功能組件可能會變得更長。這是正常的 - 不要以爲你必須當即將它分紅鉤子。 但咱們也鼓勵你開始發現自定義Hook
能夠隱藏簡單接口背後的複雜邏輯或幫助解開凌亂組件的狀況。
例如,可能有一個複雜的組件,其中包含許多以ad-hoc
方式管理的本地狀態。 useState
不會使更新邏輯更容易集中化,所以你可能但願將其 編寫爲Redux reducer
:
function todosReducer(state, action) {
switch (action.type) {
case 'add':
return [...state, {
text: action.text,
completed: false
}];
// ... other actions ...
default:
return state;
}
}
複製代碼
reducer
很是便於單獨測試,而且能夠擴展以表達複雜的更新邏輯。若有必要,能夠將它們分紅更小的reducer
。可是,你可能還享受使用React本地狀態的好處, 或者可能不想安裝其餘庫。
那麼,若是咱們能夠編寫一個useReducer Hook
,讓咱們使用reducer
管理組件的本地狀態呢?它的簡化版本可能以下所示:
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action) {
const nextState = reducer(state, action);
setState(nextState);
}
return [state, dispatch];
}
複製代碼
如今咱們能夠在咱們的組件中使用它,讓reducer
驅動它的狀態管理:
function Todos() {
const [todos, dispatch] = useReducer(todosReducer, []);
function handleAddClick(text) {
dispatch({ type: 'add', text });
}
// ...
}
複製代碼
在複雜組件中使用reducer管理本地狀態的需求很常見,咱們已經將useReducer Hook
構建到React
中。 你能夠在Hooks API參考中找到它與其餘內置Hook
一塊兒使用。