Hook
是 React 16.8
的新增特性。它可讓你在不編寫 class
的狀況下使用 state
以及其餘的 React
特性。react
這個例子用來顯示一個計數器。當你點擊按鈕,計數器的值就會增長:數組
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
就是 0。瀏覽器
你能夠在一個組件中屢次使用 State Hook
:性能優化
function ExampleWithManyStates() { // 聲明多個 state 變量! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... }
你以前可能已經在 React
組件中執行過數據獲取、訂閱或者手動修改過 DOM
。咱們統一把這些操做稱爲「反作用」,或者簡稱爲「做用」。函數
useEffect
就是一個 Effect Hook
,給函數組件增長了操做反作用的能力。它跟 class
組件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具備相同的用途,只不過被合併成了一個 API
。性能
能夠通知 React
跳過對 effect
的調用,只要傳遞數組做爲 useEffect
的第二個可選參數便可:優化
例如,下面這個組件在 React
更新 DOM
後會設置一個頁面標題:ui
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // 至關於 componentDidMount 和 componentDidUpdate: useEffect(() => { // 使用瀏覽器的 API 更新頁面標題 document.title = `You clicked ${count} times`; }[count]); // 僅在 count 更改時更新 return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
當你調用 useEffect
時,就是在告訴 React
在完成對 DOM
的更改後運行你的「反作用」函數。因爲反作用函數是在組件內聲明的,因此它們能夠訪問到組件的 props
和 state
。默認狀況下,React
會在每次渲染後調用反作用函數 —— 包括第一次渲染的時候。spa
反作用函數還能夠經過返回一個函數來指定如何「清除」反作用。例如,在下面的組件中使用反作用函數來訂閱好友的在線狀態,並經過取消訂閱來進行清除操做:code
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
的訂閱。(若是傳給 ChatAPI
的 props.friend.id
沒有變化,你也能夠告訴 React
跳太重新訂閱。)
Hook
就是 JavaScript
函數,可是使用它們會有兩個額外的規則:
有時候咱們會想要在組件之間重用一些狀態邏輯。目前爲止,有兩種主流方案來解決這個問題:高階組件和 render props
。自定義 Hook
可讓你在不增長組件的狀況下達到一樣的目的。
前面,咱們介紹了一個叫 FriendStatus
的組件,它經過調用 useState
和 useEffect
的 Hook
來訂閱一個好友的在線狀態。假設咱們想在另外一個組件裏重用這個訂閱邏輯。
首先,咱們把這個邏輯抽取到一個叫作 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
做爲參數,並返回該好友是否在線:
如今咱們能夠在兩個組件中使用它:
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
是徹底獨立的。Hook
是一種複用狀態邏輯的方式,它不復用 state
自己。事實上 Hook
的每次調用都有一個徹底獨立的 state
—— 所以你能夠在單個組件中屢次調用同一個自定義 Hook
。
自定義 Hook
更像是一種約定而不是功能。若是函數的名字以 「use」
開頭並調用其餘 Hook
,咱們就說這是一個自定義 Hook
。
useState
的替代方案。它接收一個形如 (state, action) => newState
的 reducer
,並返回當前的 state
以及與其配套的 dispatch
方法。(若是你熟悉 Redux
的話,就已經知道它如何工做了。)
在某些場景下,useReducer
會比 useState
更適用,例如 state
邏輯較複雜且包含多個子值,或者下一個 state
依賴於以前的 state
等。而且,使用 useReducer
還能給那些會觸發深更新的組件作性能優化,由於你能夠向子組件傳遞 dispatch 而不是回調函數 。
如下是用 reducer
重寫 useState
計數器示例:
const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); }