原文連接:Githubhtml
在 React v16.7.0 alpha 版本里,提出了一個新的 Feature Proposal :Hooks ,對社區以及之後前端發展所帶來的影響是巨大的。前端
學習 Hooks 的知識須要對 React 生態有較深刻的理解vue
Hooks 是 React 內部組件中的一系列特殊函數,直觀帶來的改變是引入state、生命週期函數、或者其餘 React 功能,無需使用 classes 編寫組件(類語法帶來的問題有不少),背後爲前端帶來更深刻更普及的 functional programming
思想。react
React 官方闡明瞭引入 Hooks 的動機,Hooks 出現前,咱們編寫 React 組件 會常常遇到的問題:git
It’s hard to reuse stateful logic between components
組件之間共享複用有狀態邏輯
,組件間邏輯的複用和數據傳遞就變得十分困難(必須一層一層往下傳),因此咱們使用 render props
和 higher-order components
來解決複用邏輯的同時引來了新的問題,一些無關 UI 的 wrapper 組件愈來愈多,嵌套組件愈來愈深,造成 wrapper hell
,雖然 React devTools 有過濾器來幫助咱們更容易地調試。Complex components become hard to understand
添加監聽器
,咱們須要在componentDidMount
與 componentWillUnmount
中分別編寫添加與刪除監聽器的邏輯,而通常在 componentDidMount
中,咱們也會編寫 請求數據
的邏輯。各類功能不相關聯的邏輯寫在一塊兒,並且相同功能的邏輯散落在不一樣函數內,這帶來許多隱患以及調試上的困難Classes confuse both people and machines
this
在 JS 的工做方式,例如咱們須要 綁定事件處理程序 (以何種方式綁定這裏不是重點,我的推薦箭頭函數形式);另一些重要實踐上,使用 Class 語法也帶來諸多問題,詳細參閱 classes-confuse-both-people-and-machines)目前主要的 Hooks :github
import { useState } from 'react';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div>
);
}
複製代碼
使用 state hooks
在 function components
中能夠像上面代碼這樣,等同於Class語法的代碼就不貼了。數組
值得一提的是,在 Hooks 出現以前,咱們一般叫這樣形式的組件爲
stateless components
orstateless function components
,但如今,有了 Hooks ,咱們能夠在這類組件中使用 state,因此改稱function components
。閉包
當前狀態
和 更新它的函數
。(這裏咱們使用 array destructuring
的方式將值取出來。)建立多個 state 就像這樣app
function ExampleWithManyStates() {
// Declare multiple state variables!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
複製代碼
與 this.setState
不一樣,更新狀態老是替換它而不是合併它(也解決了不少以前合併帶來的問題)less
Functional updates
若是新的 state 值是依賴上一個 state 值來計算的,咱們能夠給 setState
傳遞一個函數參數,這個函數的參數爲上一個 state 的值,返回值是更新後的 state 值,例如:
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<> Count: {count} <button onClick={() => setCount(0)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </> ); } 複製代碼
因此若是須要更新的 state 值爲 Object,咱們應該使用 object spread syntax
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
複製代碼
若是初始化的值是須要大量計算獲得的結果,可使用函數代替,此函數只會在初始化階段執行
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
複製代碼
Effect 其實就是 請求數據,操做DOM,以及訂閱事件等一系列 反作用/效果
而 useEffect 則是 以前 componentDidMount
,componentDidUpdate
和componentWillUnmount
的結合
React組件中有兩種常見的 Effect:須要清理和不須要清理的 Effect
import { 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>
);
}
複製代碼
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);
// Specify how to clean up after this effect:
return function cleanup() {
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
};
});
if (isOnline === null) {
return 'Loading...';
}
return isOnline ? 'Online' : 'Offline';
}
複製代碼
須要單獨的 API 來執行清理邏輯。由於添加和刪除訂閱的邏輯是相關的,useEffect 旨在將其保持在一塊兒。 若是 useEffect 返回一個函數,React 將在清理時執行它
清理的時機是
當組件卸載時
,但,useEffect 會在每次渲染後運行而不只僅是一次, 這就是 React 在下次執行 useEffect 以前還清除前一個 useEffect 的緣由;Using the Effect Hook – React
若是要減小 useEffect 內並非每次渲染都必要的邏輯,能夠:
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes
複製代碼
React 會比較兩次渲染的 count 值,若是同樣,就會跳過此次 useEffect
咱們能夠封裝在多個組件可重用的包含狀態的邏輯,例如
import { 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;
}
複製代碼
useFriendStatus
就是一個咱們寫好的複用邏輯函數,供其餘組件調用。
多個組件使用 相同自定義Hooks,它們的狀態和效果是 獨立隔離的,僅僅是邏輯的複用。由於本質是
調用 Custom Hooks
是調用useState
和useEffect
,它們在一個組件調用不少次,彼此產生的狀態也是徹底獨立的。
詳細參見文檔:Writing Custom Hooks – React
務必遵照的規則: Rules of Hooks – React
Hooks API: Hooks API Reference – React
React Hooks 帶來的邊際效應能夠說是巨大的,但願更加完善以後,能夠看到打開新窗的前端。
啓發 Hooks 的產生:Hooks FAQ – React
關於 Hooks 的討論:RFC: React Hooks by sebmarkbage · Pull Request #68 · reactjs/rfcs · GitHub
有趣的是,Vue的做者也很快建立了在 Vue 實驗 Hooks 的repo:GitHub - yyx990803/vue-hooks: Experimental React hooks implementation in Vue