03. 該嚐嚐React Hook了。

Hook

Hook 是 React 16.8.0 的新增特性。

Hook 使你在非 class 的情況下可以使用更多的 React 特性。Hook 不能在 class 組件中使用。

使用規則:

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

State Hook

useState

使用useState可以不通過class組件而在函數組件內使用state,可通過多次調用聲明多個state

  • 參數:

    useState() 方法裏面唯一的參數就是初始 state。

  • 返回值:

    當前 state 以及更新 state 的函數。

函數式更新:

如果新的 state 需要通過使用先前的 state 計算得出,那麼可以將函數傳遞給 setState。該函數將接收先前的 state,並返回一個更新後的值。

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
    </>
  );
}

Effect Hook

Effect Hook 可以讓你在函數組件中執行副作用操作(在 React 組件中執行過數據獲取、訂閱或者手動修改過 DOM。我們統一把這些操作稱爲“副作用”,或者簡稱爲“作用”。)

useEffect

可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。

useEffect 會在每次渲染後(第一次渲染之後和每次更新之後)都執行,如果你的 effect 返回一個函數,React 將會在組件卸載的時候執行清除操作時調用它。

useEffect在組件內可多次調用,Hook 允許我們按照代碼的用途分離他們,React 將按照 effect 聲明的順序依次調用組件中的每一個 effect。

使用位置:

組件內部調用 useEffect。 將 useEffect 放在組件內部讓我們可以在 effect 中直接訪問 count state 變量(或其他 props)。

性能優化:

useEffect 的第二個可選參數可以實現如果某些特定值在兩次重渲染之間沒有發生變化,你可以通知 React 跳過對 effect 的調用。請確保數組中包含了所有外部作用域中會隨時間變化並且在 effect 中使用的變量

// 僅在 count 更改時更新
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); 

示例代碼詳解 useStateuseEffect

// 引入 React 中的 useState Hook。它讓我們在函數組件中存儲內部 state
// 引入 useEffect
import React, { useState, useEffect } from 'react';

function Example(props) {
  // 聲明瞭一個叫 count 的 state 變量,然後把它設爲 0
  const [count, setCount] = useState(0);
  // 聲明第2個state
  const [isOnline, setIsOnline] = useState(null);

  // 無需清除的 effect
  useEffect(() => {
    // 將 document 的 title 設置爲包含了點擊次數的消息。
    document.title = `You clicked ${count} times`;
  });
  
  // 需要清除的 effect
  useEffect(() => {
    function handleFn(val) {
      setIsOnline(val);
    }
    // 註冊監聽
    XXAPI.subscribe(handleFn);
    // 清除監聽
    return () => {
      XXAPI.unsubscribe(handleFn);
    };
  });

  return (
    <div>
      // 讀取 State: 我們可以直接用 count
      <p>You clicked {count} times</p>
      // 更新 State: 可以通過調用 setCount 來更新當前的 count
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

useLayoutEffect

其函數簽名與 useEffect 相同,但它會在所有的 DOM 變更之後同步調用 effect。可以使用它來讀取 DOM 佈局並同步觸發重渲染。在瀏覽器執行繪製之前,useLayoutEffect 內部的更新計劃將被同步刷新。儘可能使用標準的 useEffect 以避免阻塞視覺更新。

與 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 調度的 effect 不會阻塞瀏覽器更新屏幕,這讓你的應用看起來響應更快。大多數情況下,effect 不需要同步地執行。在個別情況下(例如測量佈局),這時需要用到useLayoutEffect

useRef

useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化爲傳入的參數(initialValue)。返回的 ref 對象在組件的整個生命週期內保持不變。

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已掛載到 DOM 上的文本輸入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

useRef() 比 ref 屬性更有用。它可以很方便地保存任何可變值,其類似於在 class 中使用實例字段的方式。當 ref 對象內容發生變化時,useRef 並不會通知你。變更 .current 屬性不會引發組件重新渲染。

自定義Hook

自定義Hook 是一個函數,其名稱以 use 開頭(必須以 use 開頭),函數內部可以調用其他的 Hook。自定義Hook用於提取多組件之間的共享邏輯,可用於替代 render propsHOC

在需要共享邏輯的組件內調用很簡單,只需要引入定義好的自定義Hook,並傳入自己想要的參數拿到你想要的返回值作用於當前組件。

如下例:
  1. 提取自定義Hook:
import React, { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}
  1. 使用自定義Hook:
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

原文git地址 覺得有用的話,來個star鼓勵,持續更新中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章