React 18 state 狀態更新函數

參考文章

把一系列 state 更新加入隊列

設置組件 state 會把一次重新渲染加入隊列。但有時可能會希望在下次渲染加入隊列之前對 state 的值執行多次操作。為此,了解 React 如何批量更新 state 會很有幫助。

React 會對 state 更新進行批處理

在下面的示例中,可能會認為點擊 “+3” 按鈕會使計數器遞增三次,因為它調用了 setNumber(number + 1) 三次:

import { useState } from 'react';export default function Counter() {const [number, setNumber] = useState(0);return (<><h1>{number}</h1><button onClick={() => {setNumber(number + 1);setNumber(number + 1);setNumber(number + 1);}}>+3</button></>)
}

但是,每一次渲染的 state 值都是固定的,因此無論調用多少次 setNumber(1),在第一次渲染的事件處理函數內部的 number 值總是 0

setNumber(0 + 1);
setNumber(0 + 1);
setNumber(0 + 1);

但是這里還有另外一個影響因素需要討論。React 會等到事件處理函數中的 所有 代碼都運行完畢再處理 state 更新。 這就是為什么重新渲染只會發生在所有這些 setNumber() 調用 之后 的原因。

這讓你可以更新多個 state 變量——甚至來自多個組件的 state 變量——而不會觸發太多的 重新渲染。但這也意味著只有在事件處理函數及其中任何代碼執行完成 之后,UI 才會更新。這種特性也就是 批處理,它會使 React 應用運行得更快。它還會幫你避免處理只更新了一部分 state 變量的令人困惑的“半成品”渲染。

React 不會跨 多個 需要刻意觸發的事件(如點擊)進行批處理——每次點擊都是單獨處理的。請放心,React 只會在一般來說安全的情況下才進行批處理。這可以確保,例如,如果第一次點擊按鈕會禁用表單,那么第二次點擊就不會再次提交它。

在下次渲染前多次更新同一個 state

這是一個不常見的用例,但是如果想在下次渲染之前多次更新同一個 state,可以像 setNumber(n => n + 1) 這樣傳入一個根據隊列中的前一個 state 計算下一個 state 的 函數,而不是像 setNumber(number + 1) 這樣傳入 下一個 state 值。這是一種告訴 React “用 state 值做某事”而不是僅僅替換它的方法。

現在嘗試遞增計數器:

import { useState } from 'react';export default function Counter() {const [number, setNumber] = useState(0);return (<><h1>{number}</h1><button onClick={() => {setNumber(n => n + 1);setNumber(n => n + 1);setNumber(n => n + 1);}}>+3</button></>)
}

在這里,n => n + 1 被稱為 更新函數。當將它傳遞給一個 state 設置函數時:

  1. React 會將此函數加入隊列,以便在事件處理函數中的所有其他代碼運行后進行處理。
  2. 在下一次渲染期間,React 會遍歷隊列并給你更新之后的最終 state。
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);

下面是 React 在執行事件處理函數時處理這幾行代碼的過程:

  1. setNumber(n => n + 1)n => n + 1 是一個函數。React 將它加入隊列。
  2. setNumber(n => n + 1)n => n + 1 是一個函數。React 將它加入隊列。
  3. setNumber(n => n + 1)n => n + 1 是一個函數。React 將它加入隊列。

當在下次渲染期間調用 useState 時,React 會遍歷隊列。之前的 number state 的值是 0,所以這就是 React 作為參數 n 傳遞給第一個更新函數的值。然后 React 會獲取上一個更新函數的返回值,并將其作為 n 傳遞給下一個更新函數,以此類推:

更新隊列n返回值
n => n + 100 + 1 = 1
n => n + 111 + 1 = 2
n => n + 122 + 1 = 3

React 會保存 3 為最終結果并從 useState 中返回。

這就是為什么在上面的示例中點擊“+3”正確地將值增加“+3”

如果在替換 state 后更新 state 會發生什么

這個事件處理函數會怎么樣?你認為 number 在下一次渲染中的值是什么?

import { useState } from 'react';export default function Counter() {const [number, setNumber] = useState(0);return (<><h1>{number}</h1><button onClick={() => {setNumber(number + 5);setNumber(n => n + 1);}}>增加數字</button></>)
}

這是事件處理函數告訴 React 要做的事情:

  1. setNumber(number + 5)number0,所以 setNumber(0 + 5)。React 將 “替換為 5 添加到其隊列中。
  2. setNumber(n => n + 1)n => n + 1 是一個更新函數。 React 將 該函數 添加到其隊列中。

在下一次渲染期間,React 會遍歷 state 隊列:

更新隊列n返回值
“替換為 50(未使用)5
n => n + 155 + 1 = 6

React 會保存 6 為最終結果并從 useState 中返回。

注意:setState(x) 實際上會像 setState(n => x) 一樣運行,只是沒有使用 n

如果在更新 state 后替換 state 會發生什么

讓我們再看一個例子。你認為 number 在下一次渲染中的值是什么?

import { useState } from 'react';export default function Counter() {const [number, setNumber] = useState(0);return (<><h1>{number}</h1><button onClick={() => {setNumber(number + 5);setNumber(n => n + 1);setNumber(42);}}>增加數字</button></>)
}

以下是 React 在執行事件處理函數時處理這幾行代碼的過程:

  1. setNumber(number + 5)number0,所以 setNumber(0 + 5)。React 將 “替換為 5 添加到其隊列中。
  2. setNumber(n => n + 1)n => n + 1 是一個更新函數。React 將該函數添加到其隊列中。
  3. setNumber(42):React 將 “替換為 42 添加到其隊列中。

在下一次渲染期間,React 會遍歷 state 隊列:

更新隊列n返回值
“替換為 50(未使用)5
n => n + 155 + 1 = 6
“替換為 426(未使用)42

然后 React 會保存 42 為最終結果并從 useState 中返回。

總而言之,以下是可以考慮傳遞給 setNumber state 設置函數的內容:

  • 一個更新函數(例如:n => n + 1)會被添加到隊列中。
  • 任何其他的值(例如:數字 5)會導致“替換為 5”被添加到隊列中,已經在隊列中的內容會被忽略。

事件處理函數執行完成后,React 將觸發重新渲染。在重新渲染期間,React 將處理隊列。更新函數會在渲染期間執行,因此 更新函數必須是 純函數 并且只 返回 結果。不要嘗試從它們內部設置 state 或者執行其他副作用。在嚴格模式下,React 會執行每個更新函數兩次(但是丟棄第二個結果)以便幫助你發現錯誤。

命名慣例

通常可以通過相應 state 變量的第一個字母來命名更新函數的參數:

setEnabled(e => !e);
setLastName(ln => ln.reverse());
setFriendCount(fc => fc * 2);

如果你喜歡更冗長的代碼,另一個常見的慣例是重復使用完整的 state 變量名稱,如 setEnabled(enabled => !enabled),或使用前綴,如 setEnabled(prevEnabled => !prevEnabled)

摘要

  • 設置 state 不會更改現有渲染中的變量,但會請求一次新的渲染。
  • React 會在事件處理函數執行完成之后處理 state 更新。這被稱為批處理。
  • 要在一個事件中多次更新某些 state,可以使用 setNumber(n => n + 1) 更新函數。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35054.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35054.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35054.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Docker查看、創建、進入容器相關的命令

1.查看、創建、進入容器的指令 用-it指令創建出來的容器&#xff0c;創建完成之后會立馬進入容器。退出之后立馬關閉容器。 docker run -it --namec1 centos:7 /bin/bash退出容器&#xff1a; exit查看現在正在運行的容器命令&#xff1a; docker ps查看歷史容器&#xff0…

docker小白第二天

centos上安裝docker docker官網&#xff0c;docker官網&#xff0c;找到下圖中的doc文檔。 進入如下頁面 選中manuals&#xff0c;安裝docker引擎。 最終centos下的docker安裝文檔鏈接&#xff1a;安裝文檔鏈接. 具體安裝步驟&#xff1a; 1、打開Centos&#xff0c;輸入命…

【BASH】回顧與知識點梳理(十五)

【BASH】回顧與知識點梳理 十五 十五. 指令與文件的搜尋15.1 腳本文件名的搜尋which (尋找『執行檔』) 15.2 文件檔名的搜尋whereis (由一些特定的目錄中尋找文件文件名)locate / updatedbfind與時間有關的選項與使用者或組名有關的參數與文件權限及名稱有關的參數額外可進行的…

JVM垃圾回收

如何確定垃圾 對堆垃圾回收前的第一步就是要判斷哪些對象已經死亡&#xff08;即不能再被任何途徑使用的對象&#xff09; 引用計數法 這個方法就是為對象添加計數器來標識引用個數&#xff0c;計數器為 0 的對象就是不可能再被使用的。但是這種方法存在循環引用問題&#x…

布谷鳥配音:一站式配音軟件

這是一款智能語音合成軟件&#xff0c;可以快速將文字轉換成語音&#xff0c;擁有多種真人模擬發音&#xff0c;可以選擇不同男聲、女聲、童聲&#xff0c;以及四川話、粵語等中文方言和外語配音&#xff0c;并且可對語速、語調、節奏、數字讀法、多音字、背景音等進行全方位設…

less、sass的使用及其區別

CSS預處理器 CSS 預處理器是一種擴展了原生 CSS 的工具&#xff0c;它們添加了一些編程語言的特性&#xff0c;以便更有效地編寫、組織和維護樣式代碼。預處理器允許開發者使用變量、嵌套、函數、混合等功能&#xff0c;從而使 CSS 更具可讀性、可維護性和重用性&#xff0c;特…

學習筆記整理-JS-01-語法與變量

文章目錄 一、語法與變量1. 初識JavaScript2. JavaScript的歷史3. JavaScript與ECMAScript的關系4. JavaScript的體系5. JavaScript的語言風格和特性 二、語法1. JavaScript的書寫位置2. 認識輸出語句3. REPL環境&#xff0c;交互式解析器4. 變量是什么5. 重點內容 一、語法與變…

使用python快速搭建HTTP服務實現局域網網頁瀏覽或文件傳輸

1.使用命令行&#xff08;CMD&#xff09;來快速搭建一個HTTP服務器 你可以借助Python的http.server模塊。以下是在命令行中使用Python快速搭建HTTP服務器的步驟&#xff1a; 打開命令提示符&#xff08;CMD&#xff09;。 進入你想要共享文件的目錄。使用 cd 命令來切換到目…

二、編寫第一個 Spring MVC 程序

文章目錄 一、編寫第一個 Spring MVC 程序 一、編寫第一個 Spring MVC 程序 代碼示例 創建 maven 項目&#xff0c;以此項目為父項目&#xff0c;在父項目的 pom.xml 中導入相關依賴 <dependencies><dependency><groupId>junit</groupId><artifactI…

分支和循環語句(2)(C語言)

目錄 do...while()循環 do語句的語法 do語句的特點 do while循環中的break和continue 練習 goto語句 do...while()循環 do語句的語法 do 循環語句; while(表達式); do語句的特點 循環至少執行一次&#xff0c;使用的場景有限&#xff0c;所以不是經常使用。 #inc…

【uniapp】uniapp自動導入自定義組件和設置分包:

文章目錄 一、自動導入自定義組件&#xff1a;二、設置分包和預加載&#xff1a; 一、自動導入自定義組件&#xff1a; 【Volar 官網】https://github.com/vuejs/language-tools 二、設置分包和預加載&#xff1a; 【官方文檔】https://uniapp.dcloud.net.cn/collocation…

【服務平臺】Rancher運行和管理Docker和Kubernetes,提供管理生產中的容器所需的整個軟件堆棧

Rancher是一個開源軟件平臺&#xff0c;使組織能夠在生產中運行和管理Docker和Kubernetes。使用Rancher&#xff0c;組織不再需要使用一套獨特的開源技術從頭開始構建容器服務平臺。Rancher提供了管理生產中的容器所需的整個軟件堆棧。  完整軟件堆棧 Rancher是供采用容器的團…

idea添加作者信息

idea添加作者信息 自定義作者信息idea添加作者信息自定義作者信息 自定義作者信息 idea添加作者信息 在idea中&#xff0c;經常會有這些波浪紋提示&#xff0c;放在上面之后會提示添加作者信息,點擊添加作者信息后&#xff0c;但是不是自己想要的 這里提取的話好像沒什么辦法…

JavaWeb課程學習--Day01

HTML 建立css文件&#xff1a; css使用方式&#xff1a; <span>...</span>無語意包裹標簽 css中的三種選擇器&#xff1a; 注意&#xff1a;播放視音頻時要留出播放空間 盒子模型&#xff1a; 表格標簽&#xff1a; 以上表格&#xff1a; 表單標簽&#xff1a; 表…

分布式 - 服務器Nginx:一小時入門系列之動靜分離

文章目錄 1. 動靜分離的好處2. 分離靜態文件3. 修改 Nginx 配置文件4. location 命令修飾符優先級 1. 動靜分離的好處 Apache Tocmat 嚴格來說是一款java EE服務器&#xff0c;主要是用來處理 servlet請求。處理css、js、圖片這些靜態文件的IO性能不夠好&#xff0c;因此&…

ROS學習--HelloWorld的實現(C++)

1.創建工作空間并初始化 mkdir -p 自定義空間名稱/src cd 自定義空間名稱 catkin_make上述命令&#xff0c;首先會創建一個工作空間以及一個 src 子目錄&#xff0c;然后再進入工作空間調用 catkin_make命令編譯。 2.進入 src 創建 ros 包并添加依賴 cd src catkin_create_pk…

蘇紛享首屆生態人脈會成功舉辦,紛享銷客助力伙伴共同發展

近日&#xff0c;紛享銷客&蘇紛享成功舉辦了首屆生態人脈會&#xff0c;該活動于8月3日下午在蘇州東方之門舉行。本次會議匯聚了來自近20家企業的銷售精英&#xff0c;包括金蝶、泛微、夏谷、螞蟻分工、創享、黑湖智造等眾多知名企業。會議秉持著“建立生態、共同發展、深耕…

時間復雜度與空間復雜度的詳解

目錄 1.時間復雜度 2.時間復雜度計算例題 3.空間復雜度 1.時間復雜度 算法中的基本操作的執行次數&#xff0c;為算法的時間復雜度。 如何表達 時間復雜度&#xff1f; 大O的漸進表示法 實際中我們計算時間復雜度時&#xff0c;我們其實并不一定要計算精確的執行次數&#xf…

ArcGIS Pro暨基礎入門、制圖、空間分析、影像分析、三維建模、空間統計分析與建模、python融合、案例應用

GIS是利用電子計算機及其外部設備&#xff0c;采集、存儲、分析和描述整個或部分地球表面與空間信息系統。簡單地講&#xff0c;它是在一定的地域內&#xff0c;將地理空間信息和 一些與該地域地理信息相關的屬性信息結合起來&#xff0c;達到對地理和屬性信息的綜合管理。GIS的…

【數據結構】樹和二叉樹

一、樹的概念及結構 1、樹的概念 樹 是一種非線性的數據結構&#xff0c;它是由n&#xff08;n>0&#xff09;個有限結點組成一個具有層次關系的集合。把它叫做樹是因 為它看起來像一棵倒掛的樹&#xff0c;也就是說它是根朝上&#xff0c;而葉朝下的。 有一個特殊的結點&a…