React Hooks究竟是什麼呢?

来源:https://www.cnblogs.com/fundebug/archive/2019/08/21/what-is-react-hooks.html
-Advertisement-
Play Games

摘要: React Hooks原理解析。 原文: "快速瞭解 React Hooks 原理" 譯者:前端小智 我們大部分 React 類組件可以保存狀態,而函數組件不能? 並且類組件具有生命周期,而函數組件卻不能? React 早期版本,類組件可以通過繼承 來優化一些不必要的渲染,相對於函數組件,R ...


摘要: React Hooks原理解析。

我們大部分 React 類組件可以保存狀態,而函數組件不能? 並且類組件具有生命周期,而函數組件卻不能?

React 早期版本,類組件可以通過繼承PureComponent來優化一些不必要的渲染,相對於函數組件,React 官網沒有提供對應的方法來緩存函數組件以減少一些不必要的渲染,直接 16.6 出來的 React.memo函數。

React 16.8 新出來的Hook可以讓React 函數組件具有狀態,並提供類似 componentDidMountcomponentDidUpdate等生命周期方法。

類被會替代嗎?

Hooks不會替換類,它們只是一個你可以使用的新工具。React 團隊表示他們沒有計劃在React中棄用類,所以如果你想繼續使用它們,可以繼續用。

我能體會那種總有新東西要學的感覺有多痛苦,不會就感覺咱們總是落後一樣。Hooks 可以當作一個很好的新特性來使用。當然沒有必要用 Hook 來重構原來的代碼, React團隊也建議不要這樣做。

Go Go

來看看Hooks的例子,咱們先從最熟悉的開始:函數組件。

以下 OneTimeButton 是函數組件,所做的事情就是當我們點擊的時候調用 sayHi 方法。

    import React from 'react';
    import { render } from 'react-dom';
    
    function OneTimeButton(props) {
      return (
        <button onClick={props.onClick}>
            點我點我
        </button>
      )
    }
    
    function sayHi() {
      console.log('yo')
    }
    
    render(
      <OneTimeButton onClick={sayHi}/>,
      document.querySelector('#root')
    )

我們想讓這個組件做的是,跟蹤它是否被點擊,如果被點擊了,禁用按鈕,就像一次性開關一樣。

但它需要一個state,因為是一個函數,它不可能有狀態(React 16.8之前),所以需要重構成類。

函數組件轉換為類組件的過程中大概有5個階段:

  • 否認:也許它不需要是一個類,我們可以把 state 放到其它地方。
  • 實現: 廢話,必須把它變成一個class,不是嗎?
  • 接受:好吧,我會改的。
  • 努力加班重寫:首先 寫 class Thing extends React.Component,然後 實現 render等等 。
  • 最後:添加state。

    class OneTimeButton extends React.Component {
      state = {
        clicked: false
      }
    
      handleClick = () => {
        this.props.onClick();
    
        // Ok, no more clicking.
        this.setState({ clicked: true });
      }
    
      render() {
        return (
          <button
            onClick={this.handleClick}
            disabled={this.state.clicked}
          >
            You Can Only Click Me Once
          </button>
        );
      }
    }

這是相當多的代碼,組件的結構也發生了很大的變化, 我們需要多個小的功能,就需要改寫很多。

使用 Hook 輕鬆添加 State

接下來,使用新的 useState hook向普通函數組件添加狀態:

    import React, { useState } from 'react'
    
    function OneTimeButton(props) {
      const [clicked, setClicked] = useState(false)
      
      function doClick() {
        props.onClick();
        setClicked(true)
      }
    
      return (
        <button
          onClick={clicked ? undefined : doClick}
          disabled={clicked}
        >
          點我點我
        </button>
      )
    }

這段代碼是如何工作的

這段代碼的大部分看起來像我們一分鐘前寫的普通函數組件,除了useState

useState是一個hook。 它的名字以“use”開頭(這是Hooks的規則之一 - 它們的名字必須以“use”開頭)。

useState hook 的參數是 state 的初始值,返回一個包含兩個元素的數組:當前state和一個用於更改state 的函數。

類組件有一個大的state對象,一個函數this.setState一次改變整個state對象。

函數組件根本沒有狀態,但useState hook允許我們在需要時添加很小的狀態塊。 因此,如果只需要一個布爾值,我們就可以創建一些狀態來保存它。

由於Hook以某種特殊方式創建這些狀態,並且在函數組件內也沒有像setState函數來更改狀態,因此 Hook 需要一個函數來更新每個狀態。 所以 useState 返回是一對對應關係:一個值,一個更新該值函數。 當然,值可以是任何東西 - 任何JS類型 - 數字,布爾值,對象,數組等。

現在,你應該有很多疑問,如:

  • 當組件重新渲染時,每次都不會重新創建新的狀態嗎? React如何知道舊狀態是什麼?
  • 為什麼hook 名稱必須以“use”開頭? 這看起來很可疑。
  • 如果這是一個命名規則,那是否意味著我可以自定義 Hook。
  • 如何存儲更複雜的狀態,很多場景不單單隻有一個狀態值這麼簡單。

Hooks 的魔力

將有狀態信息存儲在看似無狀態的函數組件中,這是一個奇怪的悖論。這是第一個關於鉤子的問題,咱們必須弄清楚它們是如何工作的。

原作者得的第一個猜測是某種編譯器的在背後操眾。搜索代碼useWhatever並以某種方式用有狀態邏輯替換它。

然後再聽說了調用順序規則(它們每次必須以相同的順序調用),這讓我更加困惑。這就是它的工作原理。

React第一次渲染函數組件時,它同時會創建一個對象與之共存,該對象是該組件實例的定製對象,而不是全局對象。只要組件存在於DOM中,這個組件的對象就會一直存在。

使用該對象,React可以跟蹤屬於組件的各種元數據位。

請記住,React組件甚至函數組件都從未進行過自渲染。它們不直接返回HTML。組件依賴於React在適當的時候調用它們,它們返回的對象結構React可以轉換為DOM節點。

React有能力在調用每個組件之前做一些設置,這就是它設置這個狀態的時候。

其中做的一件事設置 Hooks 數組。 它開始是空的, 每次調用一個hook時,React 都會向該數組添加該 hook

為什麼順序很重要

假設咱們有以下這個組件:

    function AudioPlayer() {
      const [volume, setVolume] = useState(80);
      const [position, setPosition] = useState(0);
      const [isPlaying, setPlaying] = useState(false);
    
      .....
    }

因為它調用useState 3次,React 會在第一次渲染時將這三個 hook 放入 Hooks 數組中。

下次渲染時,同樣的3hooks以相同的順序被調用,所以React可以查看它的數組,併發現已經在位置0有一個useState hook ,所以React不會創建一個新狀態,而是返回現有狀態。

這就是React能夠在多個函數調用中創建和維護狀態的方式,即使變數本身每次都超出作用域。

多個useState 調用示例

讓咱們更詳細地看看這是如何實現的,第一次渲染:

  1. React 創建組件時,它還沒有調用函數。React 創建元數據對象和Hooks的空數組。假設這個對象有一個名為nextHook的屬性,它被放到索引為0的位置上,運行的第一個hook將占用位置0

  2. React 調用你的組件(這意味著它知道存儲hooks的元數據對象)。

  3. 調用useState,React創建一個新的狀態,將它放在hooks數組的第0位,並返回[volume,setVolume]對,並將volume 設置為其初始值80,它還將nextHook索引遞增1。
  4. 再次調用useState,React查看數組的第1位,看到它是空的,並創建一個新的狀態。 然後它將nextHook索引遞增為2,並返回[position,setPosition]
  5. 第三次調用useState。 React看到位置2為空,同樣創建新狀態,將nextHook遞增到3,並返回[isPlaying,setPlaying]

現在,hooks 數組中有3個hook,渲染完成。 下一次渲染會發生什麼?

  1. React需要重新渲染組件, 由於 React 之前已經看過這個組件,它已經有了元數據關聯。
  2. ReactnextHook索引重置為0,並調用組件。
  3. 調用useState,React查看索引0處的hooks數組,併發現它已經在該槽中有一個hook。,所以無需重新創建一個,它將nextHook推進到索引1並返回[volume,setVolume],其中volume仍設置為80
  4. 再次調用useState。 這次,nextHook1,所以React檢查數組的索引1。同樣,hook 已經存在,所以它遞增nextHook並返回[position,setPosition]
  5. 第三次調用useState,我想你知道現在發生了什麼。

就是這樣了,知道了原理,看起來也就不那麼神奇了, 但它確實依賴於一些規則,所以才有使用 Hooks 規則。

Hooks 的規則

自定義 hooks 函數只需要遵守規則 3 :它們的名稱必須以“use”為首碼。

例如,我們可以從AudioPlayer組件中將3個狀態提取到自己的自定義鉤子中:

    function AudioPlayer() {
      // Extract these 3 pieces of state:
      const [volume, setVolume] = useState(80);
      const [position, setPosition] = useState(0);
      const [isPlaying, setPlaying] = useState(false);
    
      // < beautiful audio player goes here >
    }

因此,咱們可以創建一個專門處理這些狀態的新函數,並使用一些額外的方法返回一個對象,以便更容易啟動和停止播放,例如:

    function usePlayerState(lengthOfClip) {
      const [volume, setVolume] = useState(80);
      const [position, setPosition] = useState(0);
      const [isPlaying, setPlaying] = useState(false);
    
      const stop = () => {
        setPlaying(false);
        setPosition(0);
      }
    
      const start = () => {
        setPlaying(true);
      }
    
      return {
        volume,
        position,
        isPlaying,
        setVolume,
        setPosition,
        start,
        stop
      };
    }

像這樣提取狀態的一個好處是可以將相關的邏輯和行為組合在一起。可以提取一組狀態和相關事件處理程式以及其他更新邏輯,這不僅可以清理組件代碼,還可以使這些邏輯和行為可重用。

另外,通過在自定義hooks中調用自定義hooks,可以將hooks組合在一起。hooks只是函數,當然,函數可以調用其他函數。

總結

Hooks 提供了一種新的方式來處理React中的問題,其中的思想是很有意思且新奇的。

React團隊整合了一組很棒的文檔和一個常見問題解答,從是否需要重寫所有的類組件到鉤Hooks是否因為在渲染中創建函數而變慢? 以及兩者之間的所有東西,所以一定要看看。

代碼部署後可能存在的BUG沒法實時知道,事後為瞭解決這些BUG,花了大量的時間進行log 調試,這邊順便給大家推薦一個好用的BUG監控工具 Fundebug

原文:https://daveceddia.com/intro-to-hooks/

關於Fundebug

Fundebug專註於JavaScript、微信小程式、微信小游戲、支付寶小程式、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了20億+錯誤事件,付費客戶有陽光保險、核桃編程、荔枝FM、掌門1對1、微脈、青團社等眾多品牌企業。歡迎大家免費試用


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • //直接在input標簽內加入下麵兩個事件處理程式即可 onkeyup="this.value=this.value.replace(/\D|^0/g,'')" onafterpaste="this.value=this.value.replace(/\D|^0/g,'')" ...
  • 前言 項目中需要根據坐標定位,將自己的實現過程寫下來,廢話不多說,上代碼 正文 總結 ...
  • 前言 帶你走進高德api 正文 高德api地址:https://lbs.amap.com/ 1、註冊/登錄 2、進入應用管理創建應用並申請key 3、在項目中使用 效果圖 ...
  • 通過配置nginx幹掉 304 (from memory cache) (from disk cache) ...
  • 在日常工作中,我們需要將匹配到的所有路由,映射到一個組件上。 如下代碼想要達到的效果: 不傳page和id,則映射到user預設list頁面 傳page和id,根據page不同,顯示不同的頁面 問題 使用以下代碼片段是不能實現以上效果的,因為預設情況下page和id參數是必傳的,如果不傳參數,則會根 ...
  • DOM事件,就是瀏覽器或用戶針對頁面可以做出的某種動作,我們稱這些動作為DOM事件。它是用戶和頁面交互的核心。當動作發生(事件觸發)時,我們可以為其綁定一個或多個事件處理程式(函數),來完成我們想要實現的功能。 為了方便理解下麵即將要講解的內容,在正式開始之前,讓我們先來瞭解一個最常用的事件:cli ...
  • jQuery知識梳理20190818 [TOC] 1. 時間綁定和解綁 2. 區別mouseover與mouseenter 3. 時間委托(委派/代理) 4 . 多庫共存 如果有2個庫都有$, 就存在衝突。 jQuery庫可以釋放$的使用權, 讓另一個庫可以正常使用, 此時jQuery庫只能使用jQ ...
  • JQuery Ztree 樹插件配置與應用小結 by:授客 QQ:1033553122 測試環境 Win7 jquery-3.2.1.min.js 下載地址: https://gitee.com/ishouke/front_end_plugin/blob/master/jquery-3.2.1.mi ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...