10分鐘瞭解react新特性hooks

Hook 簡介

HookReact 16.8 的新增特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。react

useState

這個例子用來顯示一個計數器。當你點擊按鈕,計數器的值就會增長:數組

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 變量

你能夠在一個組件中屢次使用 State Hook:性能優化

function ExampleWithManyStates() {
  // 聲明多個 state 變量!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

Effect Hook

你以前可能已經在 React 組件中執行過數據獲取、訂閱或者手動修改過 DOM。咱們統一把這些操做稱爲「反作用」,或者簡稱爲「做用」。函數

useEffect 就是一個 Effect Hook,給函數組件增長了操做反作用的能力。它跟 class 組件中的 componentDidMountcomponentDidUpdatecomponentWillUnmount 具備相同的用途,只不過被合併成了一個 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 的更改後運行你的「反作用」函數。因爲反作用函數是在組件內聲明的,因此它們能夠訪問到組件的 propsstate。默認狀況下,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 的訂閱。(若是傳給 ChatAPIprops.friend.id 沒有變化,你也能夠告訴 React 跳太重新訂閱。)

Hook 使用規則

Hook 就是 JavaScript 函數,可是使用它們會有兩個額外的規則:

  • 只能在函數最外層調用 Hook。不要在循環、條件判斷或者子函數中調用。
  • 只能在 React 的函數組件中調用 Hook。不要在其餘 JavaScript 函數中調用。

自定義 Hook

有時候咱們會想要在組件之間重用一些狀態邏輯。目前爲止,有兩種主流方案來解決這個問題:高階組件和 render props。自定義 Hook 可讓你在不增長組件的狀況下達到一樣的目的。

前面,咱們介紹了一個叫 FriendStatus 的組件,它經過調用 useStateuseEffectHook 來訂閱一個好友的在線狀態。假設咱們想在另外一個組件裏重用這個訂閱邏輯。

首先,咱們把這個邏輯抽取到一個叫作 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

其餘Hooks

  • useReducer

useState 的替代方案。它接收一個形如 (state, action) => newStatereducer,並返回當前的 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>
    </>
  );
}
  • useContext
相關文章
相關標籤/搜索