關於React Hooks,你不得不知的事

来源:https://www.cnblogs.com/scq000/archive/2019/03/07/10491087.html
-Advertisement-
Play Games

React Hooks是React 16.8發佈以來最吸引人的特性之一。在開始介紹React Hooks之前,讓咱們先來理解一下什麼是hooks。wikipedia是這樣給hook下定義的: In computer programming, the term hooking covers a ran ...


React Hooks是React 16.8發佈以來最吸引人的特性之一。在開始介紹React Hooks之前,讓咱們先來理解一下什麼是hooks。wikipedia是這樣給hook下定義的:

In computer programming, the term hooking covers a range of techniques used to alter or augment the behaviour of an operating system, of applications, or of other software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a hook.

通俗來說,Hook(鉤子)就是通過攔截軟體和系統內部函數調用和消息通信來增強原有功能的技術。而React Hooks想要增強哪些功能呢?設想你的項目中已經有一大堆組件,這些組件各自都擁有自己的狀態。那麼一旦你想重用某些特定的帶狀態邏輯,就得大幅度重構你的應用。現在有了React Hooks,你只需要抽離這些帶狀態的邏輯代碼,然後它們可以更好地進行重用, 而且獨立出來的代碼也更容易進行測試和管理。有了React Hooks後,你可以在函數式組件中實現之前在帶狀態組件中能做到的任何事,你能夠更靈活地實現你的應用代碼。

接下來,讓我們看看React Hooks在實際項目中到底怎麼使用。

狀態管理

對於業務性組件來說,狀態管理肯定是不可避免的。以前,我們通常寫Class組件來管理業務邏輯,或者使用redux來全局管理狀態。現在我們可以利用React Hooks新提供的State Hook來處理狀態,針對那些已經寫好的Class組件,我們也可以利用State Hook很好地進行重構, 先來看下麵這段代碼:

import React from 'react';
class Person extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
          username: "scq000"
      };
  }
  
  render() {
      return (
        <div>
            <p>Welcome to homepage. {state.username}</p>
            <input type="text" placeholder="input a username" onChange={(event) => this.setState({ username: event.target.value)})}></input>
        </div>
      );
  }
}

接下來嘗試將它重構成函數式組件:

import React, {useState} from 'react';

export const Person = () => {
  const [state, setState] = useState({username: "scq000"});
  
  return (
  	<div>
  		<p>Welcome to homepage. {state.username}</p>
		<input type="text" placeholder="input a username" onChange={(event) => setState({username: event.target.value})}></input>
  	</div>
  )
}

如上面這段代碼,我們首先使用useState api 來聲明一個內部狀態,接著聲明一個新的狀態變數state,以及它的setter方法。在這裡,為了減少重構的工作量我特意選擇了state這個變數名,你也可以單獨將每個獨立的狀態提取出來使用, 比如使用代碼const [username, setUsername] = userState("scq000")。在隨後的組件內部,我們就可以利用這個內部狀態來處理業務邏輯了。由於是函數式組件的寫法,我們也能夠避免很多this綁定,而且這部分邏輯在後續使用過程中也可以抽離出來進行重用。不過這裡有個需要註意的點是:當你使用set方法的時候,舊狀態不會自動merge到新狀態中去,所以你如果提取的狀態是個對象,且有多個屬性時,需要使用如下語法進行狀態的更新:

setState({
    ...state,
  	username: event.target.value
});

生命周期管理

我們都知道,組件的生命周期管理是整個react組件的靈魂所在。利用生命周期函數,我們可以控制整個組件的載入、更新和卸載。React Hooks中提供了Effect鉤子,使我們可以在函數式組件中實現這些功能。

為了便於理解,接下來我將分別演示如何利用Effect鉤子實現原本在Class組件中的各個生命周期方法。下麵這段代碼是我們熟悉的Class組件:

import React from 'react';
class Person extends React.Component {
  constructor(props) {
      super(props);
      this.state = {
          username: "scq000"
      };
  }
  
  componentDidMount() {
      console.log('componentDidMount: 組件載入後')
  }
  
  componentWillUnmount() {
      console.log('componentWillUnmount: 組件卸載, 做一些清理工作')
  }
  
  componentDidUpdate(prevProps, prevState) {
      if(prevState.username !== this.state.username) {
          console.log('componentDidUpdate: 更新usernmae')
      }
  }
  
  render() {
      return (
        <div>
            <p>Welcome to homepage. {state.username}</p>
            <input type="text" placeholder="input a username" onChange={(event) => this.setState({ username: event.target.value)})}></input>
        </div>
      );
  }
}

現在我們利用Effect重構一下:

import React, {useState, useEffect} from 'react';

export const Person = () => {
  const [state, setState] = useState({username: "scq000"});
  
  useEffect(() => {
      console.log('componentDidMount: 組件載入後')
      return () => {
      	console.log('componentWillUnmount: 組件卸載, 做一些清理工作')
      }
  }, []);
  
  useEffect(() => {
      console.log('componentDidUpdate: 更新usernmae')
  }, [state.username]);
  
  return (
  	<div>
  		<p>Welcome to homepage. {state.username}</p>
		<input type="text" placeholder="input a username" onChange={(event) => setState({username: event.target.value})}></input>
  	</div>
  )
}

可以看到,我們利用副作用鉤子很好地實現了原本的生命周期方法。通常我們會利用組件的生命周期函數去獲取數據,操作DOM等,而這些操作都被稱作副作用(side effect)。這些副作用邏輯一般都比較複雜,也是bug頻發的地段。 所以我們可以針對每一段邏輯單獨使用一個Effect鉤子,便於後期維護和調試。

在使用過程中,useEffect方法需要傳入兩個參數,第一個參數是回調函數:這個回調函數會在每次組件渲染後執行,包括初始化渲染以及每次更新時。另一個參數,則是狀態依賴項(數組形式),一旦檢測到依賴項數據變動,組件會更新,並且回調函數都會被再次執行一遍,從而實現componentDidUpdate的功能。如果你傳入一個空依賴,就能實現原來componentDidMount的效果,即只會執行一次。回調函數中如果返回的是閉包,這個返回的閉包函數將會在組件重新渲染前執行,所以你可以在這個位置做一些清理操作,從而實現componentWillUnmount的功能。

還有要註意的是componentWillMountcomponentWillUpdate兩個生命周期方法在新版本的React中已經不推薦使用了,具體原因可以查看這裡

至此,我們就學會如何利用Effect鉤子在函數式組件中實現所有生命周期方法,從而管理我們的應用了。

自定義Hook

重用和抽象一直都是編程中要解決的問題。我們可以自己封裝想要的Hook, 從而實現代碼邏輯的重用和抽象。

封裝自定義hook其實很簡單,就是包裝一個自定義函數,然後根據功能將其狀態和對應的effect邏輯封裝進去:

export const useFetch = (url, dependencies) => {
    const [isLoading, setIsLoading] = useState(false);
    const [response, setResponse] = useState(null);
  	const [error, setError] = useState(null);
  	
  	useEffect(() => {
      	setIsLoading(true);
        axios.get(url).then((res) => {
            setIsLoading(false);
            setResponse(res);
        }).catch((err) => {
            setIsLoading(false);
            setError(err);
        });
    }, dependencies)
    
  	return [isLoading, response, error];
}

這裡我們簡單地封裝了一個請求數據的Hook,使用方法跟其他Hook類似,直接調用就可以了:

export const Person = () => {
  const [isLoading, response, error] = useFetch("http://example.com/getPersonInfo", []); 
  
  return (
  	<div>
  		{isLoading ? 
  			<div>loading...</div>
  			:
  			(
  				error ? <div> There is an error happened. {error.message} </div>
  					  : <div> Welcome, {response.userName} </div>
  			)
  		}
  	</div>
  )
}

註意事項

在使用Hooks的過程中,需要註意的兩點是:

  • 不要在迴圈,條件或嵌套函數中調用Hook,必須始終在React函數的頂層使用Hook。這是因為React需要利用調用順序來正確更新相應的狀態,以及調用相應的鉤子函數。一旦在迴圈或條件分支語句中調用Hook,就容易導致調用順序的不一致性,從而產生難以預料到的後果。

  • 只能在React函數式組件或自定義Hook中使用Hook。

為了避免我們無意中破壞這些規則,你可以安裝一個eslint插件:

npm install eslint-plugin-react-hooks --save-dev

併在配置文件中使用它:

{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error"
  }
}

這樣,一旦你違法上述這些原則,就會獲得相應的提示。

總結

本文介紹了React Hook的使用方式,並通過幾個簡單的例子演示瞭如何在函數式組件中進行狀態管理和生命周期管理。官方目前提供了很多基礎的Hook,如useContext, useReducer, useMemo等,大家可以酌情在項目中使用。

參考資料

https://reactjs.org/docs/hooks-reference.html

——本文首發於個人公眾號,轉載請註明出處———

 

微信掃描二維碼,關註我的公眾號 最後,歡迎大家關註我的公眾號,一起學習交流。

 


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

-Advertisement-
Play Games
更多相關文章
  • Javascript中有六種數據類型 1、undefined:這個值未定義 2、boolean:這個值是布爾值 3、number:這個值是數值 4、function:這個值是函數 5、object:這個值是對象或者null,數組也可以(var e=[.......]) 6、string:這個值是字元 ...
  • 2019,不管是不是VR元年,VR行業確實在這一年勢頭凶猛,VR設備跨越式發展,然而VR內容確相對滯後。強調獨占性的各大VR內容平臺,更是將開發商分割成了不同的陣營。 ...
  • electron-vue 開發環境搭建(Windows環境) ...
  • 標題寫的全面一些,方便其他人檢索,我就是找了半天找不到資料,最後自己搞定了。 原理: 每次監聽到輸入值變化,就打一個時間戳,然後暫停2秒再去提交post驗證。 但是每次提交前,判斷一下之前打的時間戳和現在時間是否大於2秒,如果大於,則真去提交post,否則return掉不執行。 以下是代碼片段: ...
  • HTML: JS: start:就為你選擇的開始日期; end:就為你選擇的結束日期 此方式可選擇任意範圍的時間,時間格式可任意修改。 註:如果照片看不清,可以將網頁適當放大,以便觀看—謝謝。 ...
  • npm是Node Package Manager,也就是長說的NPM包管理器. 一般安裝node.js就會一起安裝. npm install npm install XXX //表示安裝模塊, 預設會安裝最新的安裝包 npm install [email protected] //表示安裝指定版本的安裝包,安裝完成 ...
  • jQuery概述 js與jQuery 獲取的對象 jQuery獲取的是jquery對象,js獲取的是js對象(dom對象),dom對象不能調用jquery的方法,jquery對象也不能調用dom對象的方法 dom對象轉換為jQuery對象:$(dom對象) 層級選擇器 子代選擇器 $("ul>li" ...
  • 組成 作用:(運營在用戶端瀏覽器) 特征:面向對象 BOM: 瀏覽器對象模型 完成視窗與視窗之間的通信,window對象是其核心對象, history【前進,後退,刷新】 是一個對象 使用【window.history】 location【地址】 DOM【】 screen【屏幕】 frames[真窗 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...