關於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
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...