React 新特性 Hooks 講解及實例(四)

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!html

使用 Ref Hooks

clipboard.png

類組件中使用 Ref 通常有:前端

  • String Ref
  • Callback Ref
  • CreateRef

上述在函數組件中沒有辦法使用它們,取而代之的是 useRef Hooks。react

useRef 主要有兩個使用場景:git

  • 獲取子組件或者 DOM 節點的句柄
  • 渲染週期之間的共享數據的存儲

你們可能會想到 state 也可跨越渲染週期保存,可是 state 的賦值會觸發重渲染,可是 ref 不會,從這點看 ref 更像是類屬性中的普通成員。github

粟例說明一下:獲取子組件或者 DOM 節點的句柄segmentfault

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 就像是能夠在其 .current 屬性中保存一個可變值的「盒子」。數組

粟例說明一下:渲染週期之間的共享數據的存儲函數

function App (props) {
  const [count, setCount] = useState(0);
  let it
  useEffect(() => {
    it = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  } , [])

  useEffect(() => {
    if (count >= 5) {
      clearInterval(it)
    }
  })

  return (
    <div style={{padding:'100px'}}>
      <h1>{count}</h1>
    </div>
  )
}

上述使用 useEffect 聲明兩個反作用,第一個每隔一秒對 count 加 1,由於只需執行一次,因此每二個參爲空數組。第二個 useEffect 判斷 count 大於等於時,中止對 count 的操做。學習

運行結果:優化

圖片描述

顯示當 count5 的時候並無中止,這是爲何呢?

由於在 clearInterval, it 這個變量已經不是 setInterval 賦值時的那個了,每次 App 重渲染都會重置它。這時候就可使用 useRef 來解決這個問題。

function App (props) {
  const [count, setCount] = useState(0);
  const it = useRef(null)
  
  useEffect(() => {
    it.current = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  } , [])

  useEffect(() => {
    if (count >= 5) {
      clearInterval(it.current)
    }
  })

  return (
    ...
  )
}

使用 useRef 來建立一個 it, 當 setInterval 返回的結果賦值給 itcurrent 屬性。

運行結果:

圖片描述

你應該熟悉 ref 這一種訪問 DOM 的主要方式。若是你將 ref 對象以 <div ref={myRef} /> 形式傳入組件,則不管該節點如何改變,React 都會將 ref 對象的 .current 屬性設置爲相應的 DOM 節點。

然而,useRef()ref 屬性更有用。它能夠很方便地保存任何可變值,其相似於在 class 中使用實例字段的方式。

這是由於它建立的是一個普通 Javascript 對象。而 useRef() 和自建一個 {current: ...} 對象的惟一區別是,useRef 會在每次渲染時返回同一個 ref 對象。

請記住,當 ref 對象內容發生變化時,useRef 並不會通知你。變動 .current 屬性不會引起組件從新渲染。若是想要在 React 綁定或解綁 DOM 節點的 ref 時運行某些代碼,則須要使用回調 ref 來實現。

自定義 Hook

前面三篇,咱們講到優化類組件的三大問題:

  • 方便複用狀態邏輯
  • 反作用的關注點分離
  • 函數組件無 this 問題

對於組件的複用狀態沒怎麼說明,如今使用自定義 Hook 來講明一下。

首先咱們把上面的例子用到 count 的邏輯的用自定義 Hook 封裝起來:

function useCount(defaultCount) {
  const [count, setCount] = useState(defaultCount);
  const it = useRef()
      
  useEffect(() => {
    it.current = setInterval(() => {
      setCount(count => count + 1)
    }, 1000)
  } , [])

  useEffect(() => {
    if (count >= 5) {
      clearInterval(it.current)
    }
  })
  
  return [count, setCount]
}


function App (props) {
  const [count, setCount] = useCount(0);

  return (
    <div style={{padding: '100px'}}>
      <h1>{count}</h1> 
    </div>
  )
}

運行效果:

圖片描述

能夠看出運行效果跟上面是同樣的。

定義 Hook 是一個函數,其名稱以 「use」 開頭,函數內部能夠調用其餘的 Hook。咱們在函數自定義寫法上彷佛和編寫函數組件沒有區別,確實自定義組件與函數組件的最大區別就是輸入與輸出的區別。

再來一個特別的 Hook 加深一下映像。在上述代碼不變的條件下,咱們在加一個自定義 Hook 內容以下:

function useCounter(count) {
  return (
    <h1>{count}</h1>
  )
}

在 App 組件調用:

function App (props) {
  const [count, setCount] = useCount(0);
  const Counter = useCounter(count)
  return (
    <div style={{padding: '100px'}}>
      {Counter}
    </div>
  )
}

運行效果:

圖片描述

咱們自定義 useCounter Hook返回的是一個 JSX,運行效果是同樣的,因此 Hook 是能夠返回 JSX 來參與渲染的,更說明 Hook 與函數組件的類似性。

使用 Hook 的法則

只在最頂層使用 Hook

不要在循環,條件或嵌套中調用 Hook,確保老是在你的 React 函數的最頂層調用他們。遵照這條規則,你就能確保 Hook 在每一次渲染中都按照一樣的順序被調用。這上 React 可以在屢次的 useState 和 useEffect 調用之間保持 hook 狀態的正確。

只在 React 函數中調用 Hook

不要在普通的 JavaScript 函數中調用 Hook, 你能夠:

  • 在 React 的函數組件中調用 Hook
  • 在自定義 Hook 中調用其它 Hook

Hooks 常見問題

如下主要說明幾個典型的問題,固然這在官網上都有說明。

生命週期方法要如何對應到 Hook?

  • constructor:函數組件不須要構造函數。你能夠經過調用 useState 來初始化 state。若是計算的代價比較昂貴,你能夠傳一個函數給 useState
  • getDerivedStateFromProps:改成 在渲染時 安排一次更新
  • shouldComponentUpdate:詳見官網.
  • render:這是函數組件體自己。
  • componentDidMount, componentDidUpdate, componentWillUnmount:useEffect Hook 能夠表達全部這些(包括 不那麼 常見 的場景)的組合。
  • componentDidCatch and getDerivedStateFromError:目前尚未這些方法的 Hook 等價寫法,但很快會加上。

clipboard.png

如何強制更新一個 Hooks 組件

若是先後兩次的值相同,useState 和 useReducer Hook 都會放棄更新。原地修改 state 並調用 setState 不會引發從新渲染。

一般,你不該該在 React 中修改本地 state。然而,做爲一條出路,你能夠用一個增加的計數器來在 state 沒變的時候依然強制一次從新渲染:

const [ignored, forceUpdate] = useReducer(x => x + 1, 0);

  function handleClick() {
    forceUpdate();
  }

可能的話儘可能避免這種模式。

交流

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

https://github.com/qq44924588...

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

clipboard.png

相關文章
相關標籤/搜索