React Hooks 你不來瞭解下?

来源:https://www.cnblogs.com/dreamcc/archive/2019/09/16/11529193.html
-Advertisement-
Play Games

前言 最近在看 React 的新語法—— ,只能一句話概括:React 語法真的是越來越強大,越寫代碼越少。 強烈推薦還沒看 React Hooks 的同學去學習下,這會讓你寫react 項目變得非常爽! 以前 React 組件可以看成是: 無狀態組件(function定義)和有狀態組件(class ...


前言

最近在看 React 的新語法—— React Hooks,只能一句話概括:React 語法真的是越來越強大,越寫代碼越少。

強烈推薦還沒看 React Hooks 的同學去學習下,這會讓你寫react 項目變得非常爽!

以前 React 組件可以看成是: 無狀態組件(function定義)和有狀態組件(class 定義),React Hooks 出現之後,我們基本所有的組件都可以用function定義,包括有組態組件,基本廢除了 寫 class 語法的 複雜性,讓我們寫代碼真正變成了函數式編程。

狀態 Hooks(useState)

這裡說明一點,react中所有的Hooks都是一種函數,函數都是用來實現特定功能的。

useState 提供了創建組件state的功能,用法:

const [count, setCount] = useState(0)

useState() 接受唯一一個狀態初始值參數,返回包含狀態和改變狀態對應的函數的數組,這裡採用 數組解構方法獲得 狀態變數 count ,改變狀態方法 setCount。

強調一點:

useState() 傳入的初始值不一定非要是個對象,可以為普通數據類型,比如:Number,String等,初始值用作組件初次渲染。

setCount() 接受一個全新的state狀態,react會直接全部替換掉原來的state狀態,這點和 setState() 有所不同。

Example:

import React, { useState } from 'react';
function Example(){
  const [ count , setCount ] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()=>{setCount(count+1)}}>click me</button>
    </div>
  )
}
export default Example;

當把 Example 組件渲染到頁面上時,可以通過點擊按鈕實時改變 count 狀態,react 也會根據狀態改變重新渲染頁面。

執行副作用操作Hooks(useEffect)

之前在寫 react 組件時,往往我們在組件的生命周期函數裡面做一些額外的操作,比如發送ajax請求獲取數據,清除定時器和非同步執行任務等。

我們發現這樣操作重覆的代碼很多,而且如果對於 react 生命周期鉤子不熟悉的話,很容易出錯,於是乎,useEffect() Hooks 出現了。

useEffect() hooks 可以允許我們在 react 函數式組件中執行一些額外的副作用操作。

在 React 組件中有兩種常見副作用操作:

  • 需要清除的,比如開啟的定時器,訂閱外部數據源等,這些操作如果在組件消亡後不及時清除會導致記憶體泄漏。

  • 不需要清除的,比如發起網路請求,手動變更 DOM,記錄日誌等。

在 react 官網中有一段話很重要:

如果你熟悉 React class 的生命周期函數,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。

Example:


import React, { useState , useEffect } from 'react';
function Example(){
  const [ count , setCount ] = useState(0);
  useEffect(()=>{
    console.log(`You clicked ${count} times`)
  })
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()=>{setCount(count+1)}}>click me</button>
    </div>
  )
}
export default Example;

我們在剛纔的例子上新增了一個功能,每次我們 count 狀態改變的時候都會在控制台列印出我們點擊的次數。可以看到使用 useEffect() Hooks 輕鬆實現。

強調一點:

  • react首次渲染和之後的每次渲染都會調用一遍useEffect函數,而之前我們要用兩個生命周期函數分別表示首次渲染(componentDidMonut)和更新導致的重新渲染(componentDidUpdate)。

  • useEffect中定義的函數的執行不會阻礙瀏覽器更新視圖,也就是說這些函數是非同步執行的,而componentDidMonut和componentDidUpdate中的代碼都是同步執行的。

  • 可以為 useEffect() 傳入第二個參數,它是一個數組,數組裡面表示這個副作用的操作依賴的狀態變數,換句話說:如果這個副作用的操作依賴的狀態變數沒有改變,則不會執行副作用操作。

組件之間傳值Hooks(useContext)

之前在用類聲明組件時,父子組件的傳值是通過組件屬性和props進行的,那現在使用方法(Function)來聲明組件,已經沒有了constructor構造函數也就沒有了props的接收,那父子組件的傳值就成了一個問題。React Hooks 為我們準備了useContext。

useContext它可以幫助我們跨越組件層級直接傳遞變數,實現共用。

Example:

一:利用 createContext 創建上下文


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

// 創建一個 CountContext
const CountContext = createContext()

function Example(){
  const [ count , setCount ] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()=>{setCount(count+1)}}>click me</button>
      {/* 將 context 傳遞給 子組件,context 值由value props決定*/}
      <CountContext.Provider value={count}>
        <Counter/>
      </CountContext.Provider>
    </div>
  )
}
export default Example;

二:使用useContext 獲取上下文

對於要接收context的後代組件,只需引入 useContext() Hooks 即可。


function Counter(){
  const count = useContext(CountContext)  //一句話就可以得到count
  return (<h2>{count}</h2>)
}

強調一點:

useContext 的參數必須是 context 對象本身:

  • 正確: useContext(MyContext)
  • 錯誤: useContext(MyContext.Consumer)
  • 錯誤: useContext(MyContext.Provider)

當組件上層最近的 <MyContext.Provider> 更新時,該 Hook 會觸發重渲染,並使用最新傳遞給 <MyContext.Provider> 的 context value 值。

處理更為複雜state結構的Hooks(useReducer)

前面我們使用的 useState Hooks可以為組件提供 state和操作改變state狀態的方法,但往往有時候我們的state 結構更為複雜,例如 state 包含多個子值,或者下一個 state 依賴於之前的 state 等,這時候 useReducer 比 useState 更合適。

useReducer 的用法:


const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducer 接收一個形如 (state, action) => newState 的 reducer,並返回當前的 state 以及與其配套的 dispatch 方法。

我們可以使用 useReducer 來重新寫我們開篇計數器的demo:

Example:


const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      return state
  }
}
// 定義 Counter 組件
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </div>
  );
}

對於熟悉 redux 的伙伴,這種寫法很容易就能理解,每次通過點擊按鈕分發一個action來更新state狀態數據。

強調一點:

React 不使用 state = initialState 這一由 Redux 推廣開來的參數約定。有時候初始值依賴於 props,因此需要在調用 Hook 時指定。如果你特別喜歡上述的參數約定,可以通過調用 useReducer(reducer, undefined, reducer) 來模擬 Redux 的行為,但我們不鼓勵你這麼做。

這裡是 react 官網提供的一句話,也就是說我們為 state 提供初始值的時候不能夠像 redux 中利用 ES6 預設參數來指定,必須得通過 useReducer來指定。這個切記,因為我也踩過坑!

對於 useReducer 和 useState的區別主要是以下兩點:

  • 當 state 狀態值結構比較複雜時,使用 useReducer 更有優勢。
  • 使用 useState 獲取的 setState 方法更新數據時是非同步的;而使用 useReducer 獲取的 dispatch 方法更新數據是同步的。

性能優化Hooks(useMemo)

我們知道 使用class 形式的組件有著生命周期 shouldCompnentUpdate函數用來優化組件的性能,防止每次渲染造成昂貴的開銷。
使用function的形式來聲明組件,失去了shouldCompnentUpdate(在組件更新之前)這個生命周期,也就是說我們沒有辦法通過組件更新前條件來決定組件是否更新。而且在函數組件中,也不再區分mount和update兩個狀態,這意味著函數組件的每一次調用都會執行內部的所有邏輯,就帶來了非常大的性能損耗。

useMemo主要用來解決使用React hooks產生的無用渲染的性能問題。

舉個例子:

在 A 組件中有兩個子組件 B 和 C,當 A 組件中傳給 B 的 props 發生變化時,A 組件狀態會改變,重新渲染。此時 B 和 C 也都會重新渲染。其實這種情況是比較浪費資源的,現在我們就可以使用 useMemo 進行優化,B 組件用到的 props 變化時,只有 B 發生改變,而 C 卻不會重新渲染。

Example:


// A 組件

import React from 'react';

export default ({ text }) => {
  console.log('component A:', 'render');
  return <div> A 組件:{ text }</div>
}

// B 組件

import React from 'react';

export default ({ text }) => {
  console.log('component B:', 'render');
  return <div> B 組件:{ text }</div>
}

// App 組件

import React, { useState, useMemo } from 'react';
import A from './ExampleA';
import B from './ExampleB';

export default () => {
  const [a, setA] = useState('A');
  const [b, setB] = useState('B');

  const exampleA = useMemo(() => <A text={a} />, [a]);
  const exampleB = useMemo(() => <B text={b} />, [b]);

  return (
    <div>
      { exampleA }
      { exampleB }
        <br />
        <button onClick={ () => setA('A改變了') }>修改傳給 A 的屬性</button>
        &nbsp;&nbsp;&nbsp;&nbsp;
        <button onClick={ () => setB('B改變了') }>修改傳給 B 的屬性</button>
    </div>
  )
}

我們點擊不同的按鈕,控制台都只會列印一條輸出,改變 a 或者 b,A 和 B 組件都只有一個會重新渲染。

其實 React 還有很多常用的 Hooks,比如用來獲取DOM元素 和保存變數的useRef,優化函數式組件性能的useCallback等,對於這些的Hooks大家可以去react 官網瞭解下,相信大家應該也能搞清楚,這裡就不做進一步探討。

React Hooks Api

結語

本篇文章出自於我們 web-study 倉庫,如果喜歡的話,歡迎給個 star!

地址: web-study


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

-Advertisement-
Play Games
更多相關文章
  • 動態原型模型 組合使用構造函數模型和原型模型,使得OO語言程式員在看到獨立的構造函數和原型時很困惑。動態原型模型致力於解決該問題,它把所有的信息封裝在構造函數中,通過在構造函數中初始化原型(僅在必要情況下),同時又使用構造函數和原型的優點。 實例代碼如下: 寄生構造函數模型 基本思想:創建一個函數, ...
  • 該模型為創建自定義類型最常用的方式。 部分摘自《JavaScript高級程式設計(第3版)》 ...
  • 今天在學習查閱代碼的時候,發現了一個不認識的CSS代碼,於是進行學習。 效果圖 使圖片在對應的元素內以不同的方式進行垂直對齊 ...
  • 工廠模型抽象了創建具體對象的過程,以下示例代碼說明工廠類型: 部分摘自《JavaScript高級程式設計(第3版)》 ...
  • JavaScript 基礎入門 JavaScript 的組成 JS 由三部分組成,它們分別是:ECMAScript、DOM、BOM. ECMAScript 因為網景開發了JavaScript,一年後微軟又模仿JavaScript開發了JScript,為了讓JavaScript成為全球標準,幾個公司聯 ...
  • <!DOCTYPE>標簽的定義 <!DOCTYPE> 聲明位於文檔中最前面的位置,處於<html>標簽之前。<!DOCTYPE> 聲明不是一個HTML標簽,它是告知Web瀏覽器該頁面使用了哪種HTML版本(規範),瀏覽器用此版本(規範)對該文檔進行解析,渲染。 <!DOCTYPE> 可聲明的 DTD ...
  • 利用快速原型獲取軟體需求 在客戶 開發商 用戶 之間 旨在啟發客戶 引導客戶真正知道和瞭解自己的可行需求 快速搭建原型 高效準確地確定用戶需求 我們可能要用到一個軟體需求方面的知識-快速原型 例子 以CSS語言為例從一個最最最的教務系統入手 代碼如下: <html><center><h2>歡迎返校! ...
  • 以下通過一段示例代碼,說明原型模型中的基本概念以及知識點。 部分摘自《JavaScript高級程式設計(第3版)》 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...