React Hooks (Proposal)

原文連接:Githubhtml

React Hooks (Proposal)

在 React v16.7.0 alpha 版本里,提出了一個新的 Feature Proposal :Hooks ,對社區以及之後前端發展所帶來的影響是巨大的。前端

學習 Hooks 的知識須要對 React 生態有較深刻的理解vue

What is the Hooks ?

Hooks 是 React 內部組件中的一系列特殊函數,直觀帶來的改變是引入state、生命週期函數、或者其餘 React 功能,無需使用 classes 編寫組件(類語法帶來的問題有不少),背後爲前端帶來更深刻更普及的 functional programming 思想。react

引入 Hooks 的動機

React 官方闡明瞭引入 Hooks 的動機,Hooks 出現前,咱們編寫 React 組件 會常常遇到的問題:git

  1. It’s hard to reuse stateful logic between components
    • React 沒有提供官方方案去解決 組件之間共享複用有狀態邏輯 ,組件間邏輯的複用和數據傳遞就變得十分困難(必須一層一層往下傳),因此咱們使用 render propshigher-order components 來解決複用邏輯的同時引來了新的問題,一些無關 UI 的 wrapper 組件愈來愈多,嵌套組件愈來愈深,造成 wrapper hell ,雖然 React devTools 有過濾器來幫助咱們更容易地調試。
    • 使用 Hooks 能夠在不改變組件層次結構的狀況下複用有狀態邏輯。能夠利用 custom hooks,複用包含狀態的邏輯,這些邏輯再也不出如今組件樹中,而是造成一個獨立、可測試的單元,但仍然響應 React 在渲染之間的變化;社區之間分享 自定義hooks 更容易,hooks 就像插件同樣。
  2. Complex components become hard to understand
    • 隨着項目深刻,咱們逐漸會編寫愈來愈複雜的邏輯在組件中,這致使了再生命週期函數內編寫的邏輯很是臃腫,例如 添加監聽器,咱們須要在componentDidMountcomponentWillUnmount 中分別編寫添加與刪除監聽器的邏輯,而通常在 componentDidMount 中,咱們也會編寫 請求數據 的邏輯。各類功能不相關聯的邏輯寫在一塊兒,並且相同功能的邏輯散落在不一樣函數內,這帶來許多隱患以及調試上的困難
    • 使用 Hooks 能夠 將相關聯的邏輯code由組件拆分出來成更簡單直觀的函數(例如訂閱事件、請求數據)
  3. Classes confuse both people and machines
    • React 官方認爲 JS 的 Class 語法的學習成本很高,使用類語法,要必須清楚 this 在 JS 的工做方式,例如咱們須要 綁定事件處理程序 (以何種方式綁定這裏不是重點,我的推薦箭頭函數形式);另一些重要實踐上,使用 Class 語法也帶來諸多問題,詳細參閱 classes-confuse-both-people-and-machines)
    • 使用 Hooks 能夠 在無需編寫 Class 語法的狀況下 引入state、生命週期函數、或者其餘 React 功能

實際上引入 Hooks 並不會給現有的代碼帶來問題

  1. 徹底可選(將使用 Hooks 的選擇權交給開發者)
  2. 向後兼容(不會有任何破壞性更改)
  3. 在可預見的將來內,不會從 React 中刪除 類語法
  4. Hooks 並無顛覆以前的 React 概念。相反,帶來更直觀的 API 實現相同的功能

編寫 Hooks

目前主要的 Hooks :github

  1. State hooks
  2. Effect hooks
  3. Custom hooks (自定義 hooks 用來複用包含狀態的邏輯)

useState

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 hooksfunction components中能夠像上面代碼這樣,等同於Class語法的代碼就不貼了。數組

值得一提的是,在 Hooks 出現以前,咱們一般叫這樣形式的組件爲 stateless components or stateless function components ,但如今,有了 Hooks ,咱們能夠在這類組件中使用 state,因此改稱 function components閉包

  1. useState 的參數是 咱們須要定義的 state 名的初始值(沒必要像之前同樣,state 必須爲 Object,若是咱們想要建立兩個state,就調用兩次 useState)
  2. 返回值是包含兩個值的數組,兩個值分別爲 當前狀態更新它的函數 。(這裏咱們使用 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};
});
複製代碼

延遲初始化 state

若是初始化的值是須要大量計算獲得的結果,可使用函數代替,此函數只會在初始化階段執行

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});
複製代碼

useEffect

Effect 其實就是 請求數據,操做DOM,以及訂閱事件等一系列 反作用/效果

而 useEffect 則是 以前 componentDidMountcomponentDidUpdatecomponentWillUnmount 的結合

React組件中有兩種常見的 Effect:須要清理和不須要清理的 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>
  );
}
複製代碼
  1. 將在每次渲染後執行 useEffect
  2. useEffect 寫在 函數內部是爲了直接訪問到state值,利用了閉包的性質,不須要額外 API

須要清理的 Effect

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

Custom Hooks

咱們能夠封裝在多個組件可重用的包含狀態的邏輯,例如

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 是調用 useStateuseEffect,它們在一個組件調用不少次,彼此產生的狀態也是徹底獨立的。

詳細參見文檔:Writing Custom Hooks – React

使用 Hooks 的規則:

務必遵照的規則: Rules of Hooks – React

Hooks API: Hooks API Reference – React

Conclusion

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

原文連接:Github

相關文章
相關標籤/搜索