React(四)setState原理-性能優化-ref

setState詳解

實現原理

開發中我們并不能直接修改State來重新渲染界面:

因為修改State之后,希望React根據最新的State來重新渲染界面,但這種方式的修改React并不知道數據發生了變化;

React并沒有類似于Vue2中的Object.defineProperty或Vue3中的Proxy的方式來監聽數據的變化;

我們必須通過setState來告知React數據已經發生了變化;

該方法的源碼實現如下:

三種使用方式

import React, { Component } from 'react'export class App extends Component {constructor () {super () this.state={message:'hello',count:20}}changeText() {// 1.setState更多用法// 1.1基本使用// this.setState({//   message:'你好'// })/* 1.2setState可以傳入一個回調函數好處一:可在回調函數中編寫新的state處理邏輯好處二:當前回調函數會將之前的state和props傳遞進來*/// this.setState((state,props) => {//   console.log(state,props);//   return {//     message:'你好'//   }// })/* 1.3setState在React的事件處理中是一個異步調用如果希望在數據更新之后(數據合并),獲取到對應的結果執行一些邏輯代碼那么可以在setState中傳入第二個參數:callback*/this.setState({message:"你好呀,小橙子"},() => {console.log(this.state.message);//你好呀,小橙子})// 會先執行這行代碼console.log("-----------",this.state.message);// hello}addCount () {}render() {const { message,count } = this.statereturn (<div><h2>{message}</h2><button onClick={() => this.changeText()}>修改文本</button><h2>當前計數:{count}</h2><button onClick={() => this.addCount()}>count+1</button></div>)}
}export default App

setState異步更新

為什么setState設計為異步呢?

React核心成員(Redux的作者)Dan Abramov也有對應的回復:RFClarification: why is `setState` asynchronous? · Issue #11527 · facebook/react · GitHub

  1. setState設計為異步,可以顯著的提升性能;
  • 如果每次調用 setState都進行一次更新,那么意味著render函數會被頻繁調用,界面重新渲染,這樣效率是很低的;
  • 最好的辦法應該是獲取到多個更新,之后進行批量更新;
  1. 如果同步更新了state,但是還沒有執行render函數,那么state和props不能保持同步;
  • state和props不能保持一致性,會在開發中產生很多的問題;
import React, { Component } from 'react'
function Hello (props) {return <h2>{props.message}</h2>
}export class App extends Component {constructor () {super () this.state={message:'hello',count:20}}changeText() {}addCount () {// 一直是20+1——也說明setState是異步的/* this.setState({count:this.state.count + 1})this.setState({count:this.state.count + 1})this.setState({count:this.state.count + 1}) */this.setState((state) => {console.log(this.state.count);//20return {count:state.count + 1}})this.setState((state) => {console.log(this.state.count);//20return {count:state.count + 1}})this.setState((state) => {console.log(this.state.count);//20return {count:state.count + 1}})}/* 假設setState是同步的,那么點擊按鈕后,this.state.message已經改變——但是還未執行render函數——那么頁面上的數據就還是之前的-即state和props不能保持同步*/render() {// 調用addCount函數,里面有三個setState,但render只會重新渲染一次——批量更新console.log("render函數被執行了幾次");const { message,count } = this.statereturn (<div><h2>{message}</h2><button onClick={() => this.changeText()}>修改文本</button><h2>當前計數:{count}</h2><button onClick={() => this.addCount()}>count+1</button><Hello message={message} /></div>)}
}export default App

如何獲取異步修改完的結果?

方式一:setState的回調

setState接受兩個參數:第二個參數是一個回調函數,這個回調函數會在更新后會執行;

方式二:生命周期函數

注意:在React18之前,如果在setState外邊包個setTimeout這種宏任務,它不由React回調,而是瀏覽器。故setState就變成了同步操作

React18之前:setTimeout中setState操作,是同步操作
React18之后:setTimeout中setState操作,是異步操作
setTimeout ( () => {this.setState({message:'你好'})console.log(this.state.message);//React18之前:你好;React18之后:hello
},0) 

性能優化SCU

當我們修改根組件中的數據,所有組件都需要重新render,進行diff算法,性能極低!——只更新數據改變的即可——通過shouldComponentUpdate方法

//nextProps:修改之后最新的props; nextState:修改之后最新的state
shouldComponentUpdate(nextProps, nextState) {// 性能優化:自己對前后state進行對比,如果前后state沒有變化,就不更新組件if(this.state.message !== nextState.message  || this.state.count !== nextState.count){return true}//根據返回值覺得是否調用render方法return false
}

但如果所有的類都需要手動設置,那工作量也太大了!

React內部已幫我們實現PureComponent

底層原理實現:

注意:PureComponent只能檢測第一層數據的變化,也就是復雜數據類型若地址未發生變化,是檢測不到的

import React, { PureComponent } from 'react'export default class App extends PureComponent {constructor () {super()this.state = {books:[{name:"你不知道的JS",price:99,count:2},{name:"JS高級程序設計",price:78,count:2},{name:"React高級設計",price:94,count:2},{name:"LeetCode",price:88,count:2},]}}/* PureComponent底層實現原理:shouldComponentUpdate(nextProps,nextState) {淺層比較return (!shallowEqual(nextProps,this.props) || shallowEqual(nextState,this.state))}*/addBook() {const newBook = {name:"算法",price:99,count:8}/* React 依賴于狀態的不可變性來確定何時需要更新 UI直接修改狀態對象的屬性不會讓 React 知道狀態已經發生了變化,因此不會重新渲染組件該做法在PureComponent中是不能引起重新渲染的this.state.books.push(newBook)  */this.setState({books:[...this.state.books,newBook]})}addCount (index) {// this.state.books[index]++   //引用對象并未發生改變const newBooks = [...this.state.books]//引用發生改變newBooks[index].count++this.setState({books:newBooks})}render() {const {books} = this.statereturn (<div><h2>書籍列表</h2><ul>{books.map((item,index) => {return (<li key={item.name}><span>{item.name}-{item.price}-{item.count}</span><button onClick={() => this.addCount(index)}>+1</button></li>)})}</ul><button onClick={() => this.addBook()}>添加新書籍</button></div>)}
}

針對類組件可使用 PureComponent。那函數組件那???

我們可使用高階函數memo將組件都包裹一層

import { memo } from "react";
const Profile = memo(function(props) {console.log("Profile render函數");return (<div><h2>Profile-{props.message}</h2></div>)
})export default Profile

ref獲取元素或組件實例

傳入一個對象

1.類組件:通過 React.createRef() 創建 ref 對象,并綁定到 JSX 元素的 ref 屬性

class ClassComponent extends React.Component {constructor(props) {super(props);this.myRef = React.createRef(); // 創建 ref 對象}componentDidMount() {console.log(this.myRef.current); // 獲取 DOM 元素}render() {return <div ref={this.myRef}>類組件中使用 ref 對象</div>;}
}

2.函數組件:通過 React.useRef() Hook 創建 ref 對象,并綁定到 JSX 元素

function FunctionComponent() {const myRef = React.useRef(null); // 創建 ref 對象React.useEffect(() => {console.log(myRef.current); // 獲取 DOM 元素}, []);return <div ref={myRef}>函數組件中使用 useRef</div>;
}

使用回調 ref

原理:將回調函數傳遞給元素的 ref 屬性,React 在掛載/卸載時調用該回調,參數為 DOM 元素

1.類組件

class ClassComponent extends React.Component {constructor(props) {super(props);this.myRef = null; // 直接保存 DOM 引用}componentDidMount() {console.log(this.myRef); // 直接訪問 DOM 元素}render() {return (<div ref={(el) => { this.myRef = el; }}> {/* 回調 ref */}類組件中使用回調 ref</div>);}
}

2.函數組件:

function FunctionComponent() {const myRef = React.useRef(null); // 持久化保存 DOM 引用const setRef = (el) => {myRef.current = el; // 通過回調更新 ref};React.useEffect(() => {console.log(myRef.current); // 獲取 DOM 元素}, []);return <div ref={setRef}>函數組件中使用回調 ref</div>;
}

獲取函數子組件的DOM

import React, { PureComponent, createRef,forwardRef } from 'react'const HelloWorld = forwardRef(function(props,ref) {return(<h1 ref={ref}>Hello World</h1>)
})export class App extends PureComponent {constructor(props) {super(props);this.whyRef = createRef();}getNativeDOM(){console.log(this.whyRef.current);}render() {return (<div>{/* 不能在函數組件上直接使用 ref屬性,因為他們沒有實例——通過forwardRef做一個轉發*/}<HelloWorld ref={this.whyRef}/><button onClick={() => this.getNativeDOM()}>獲取組件實例</button></div>)}
}export default App

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

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

相關文章

SSH密鑰認證 + 文件系統權限控制 + Git倉庫配置+封存與解封GIT倉庫

在本地服務器上實現多個用戶僅通過git push操作修改倉庫、禁止其他改寫方式的需求&#xff0c;可以通過以下步驟實現&#xff1a; 方法概述 通過SSH密鑰認證 文件系統權限控制 Git倉庫配置&#xff0c;確保用戶僅能通過git push命令提交修改&#xff0c;而無法通過直接操作服…

全文通讀:126頁華為IPD集成產品開發與DFX實戰【文末附可編輯PPT下載鏈接】

綁定資料內容: 12023華為流程體系及落地實施【108頁 PPT】.pptx22024版基于華為IPD與質量管理體系融合的研發質量管理【63頁】.pptx

//TODO 動態代理的本質?

待解決 //TODO 面試題 為啥mybatis的mapper只有接口沒有實現類&#xff0c;但它卻能工作&#xff1f;?(ai參考,待深究源碼) 1. 動態代理生成代理對象 MyBatis 使用 JDK 動態代理 為每個 Mapper 接口生成代理對象&#xff1a; ? 核心類&#xff1a;MapperProxy&#xff08;…

C++11中智能指針的使用(shared_ptr、unique_ptr、weak_ptr)

C11中智能指針的使用(shared_ptr、unique_ptr、weak_ptr) 一、shared_ptr原理 shared_ptr 是另一種智能指針&#xff0c;用于實現多個 shared_ptr 實例共享同一個對象的所有權。它通過內部的控制塊&#xff08;通常是一個包含計數器和指向對象的指針的結構&#xff09;來管理…

2024年認證杯SPSSPRO杯數學建模B題(第二階段)神經外科手術的定位與導航全過程文檔及程序

2024年認證杯SPSSPRO杯數學建模 B題 神經外科手術的定位與導航 原題再現&#xff1a; 人的大腦結構非常復雜&#xff0c;內部交織密布著神經和血管&#xff0c;所以在大腦內做手術具有非常高的精細和復雜程度。例如神經外科的腫瘤切除手術或血腫清除手術&#xff0c;通常需要…

嘗試在軟考62天前開始成為軟件設計師-信息系統安全

安全屬性 保密性:最小授權原則(能干活的最小權限)、防暴露(隱藏)、信息加密、物理保密完整性(防篡改):安全協議、校驗碼、密碼校驗、數字簽名、公證 可用性:綜合保障( IP過濾、業務流控制、路由選擇控制、審計跟蹤)不可抵賴性:數字簽名 對稱加密 DES :替換移位 3重DESAESR…

Rocky9.5基于sealos快速部署k8s集群

首先需要下載 Sealos 命令行工具&#xff0c;sealos 是一個簡單的 Golang 二進制文件&#xff0c;可以安裝在大多數 Linux 操作系統中。 以下是一些基本的安裝要求&#xff1a; 每個集群節點應該有不同的主機名。主機名不要帶下劃線。 所有節點的時間需要同步。 需要在 K8s …

G口服務器和普通服務器之間的區別

今天小編主要來為大家介紹一下G口服務器和普通服務器之間的區別&#xff01; 首先&#xff0c;從硬件配置上看&#xff0c;普通服務器通常都會配備中央處理器、內存和硬盤等基本的硬件配置&#xff0c;能夠適用于各種應用程序和服務&#xff1b;G口服務器除了基礎的硬件配置還增…

Cursor軟件如何刷新機器碼流程

一.退出Cursor軟件賬號 打開Cursor軟件&#xff0c;點擊設置-->General-->Account-->Log out,現將Cursor軟件上登錄的賬戶退出。 二.將Cursor官網上登錄的Cursor賬戶也清空掉 點擊頭像--> ACCOUNT SETTINGS -->Account-->Advanced-->Delete Account-->…

類與對象(中)(詳解)

【本節目標】 1. 類的6個默認成員函數 2. 構造函數 3. 析構函數 4. 拷貝構造函數 5. 賦值運算符重載 6. const成員函數 7. 取地址及const取地址操作符重載 1.類的6個默認成員函數 如果一個類中什么成員都沒有&#xff0c;簡稱為空類。 空類中真的什么都沒有嗎&…

開發語言漫談-groovy

groovy是一門腳本語言&#xff0c;在前期的腳本語言中簡單介紹了下。現在再深入介紹下&#xff0c;因為它是本平臺上選用的腳本語言。所謂腳本語言就是不用編譯&#xff0c;直接執行。這種特色非常適合做嵌入編程&#xff0c;即編即用。我們知道平臺后臺的業務開發語言是Java&a…

React+Ant Design的Layout布局實現暗黑模式切換

目錄 效果預覽完整代碼我遇到的BUG問題代碼BUG1&#xff1a;暗黑模式下內容區不變成深色BUG2&#xff1a;光亮模式下的左右區域是深色 補充知識ConfigProvider是什么&#xff1f;Ant Design中的theme如何使用&#xff1f;theme 配置的常見字段主題算法通過 useToken 獲取主題 效…

TCP 三次握手與四次揮手過程

TCP 作為一種面向連接的、可靠的傳輸層協議&#xff0c;其連接管理機制對于保障數據的可靠傳輸至關重要。 三次握手&#xff08;建立連接&#xff09; 三次握手是 TCP 建立連接時所采用的機制&#xff0c;其目的在于確保客戶端和服務器雙方都具備發送和接收數據的能力&#x…

【線程安全的單例模式和STL是否是線程安全/智能指針是否是線程安全】

文章目錄 一、單例模式的特點二、餓漢模式實現單例三、懶漢模式實現單例四、STL線程安全嗎&#xff1f;五、智能指針線程安全嗎&#xff1f; 一、單例模式的特點 一個類&#xff0c;只應該實例化了一個對象&#xff0c;就是單例。 二、餓漢模式實現單例 舉個餓漢模式的例子&…

力扣DAY24 | 熱100 | 回文鏈表

前言 簡單 √ 是反轉鏈表的衍生題&#xff0c;很快寫完了。不過沒考慮到恢復鏈表結構的問題。 題目 給你一個單鏈表的頭節點 head &#xff0c;請你判斷該鏈表是否為回文鏈表。如果是&#xff0c;返回 true &#xff1b;否則&#xff0c;返回 false 。 示例 1&#xff1a; 輸…

【GL010】C++

1.C中的const關鍵字有哪些用法&#xff1f; 1.修飾變量&#xff1a;表示變量的值不可修改。 const int a 10; 2.修飾指針&#xff1a; const int* p&#xff1a; // 指針指向的內容不可修改。 int* const p&#xff1a; // 指針本身不可修改。 const int* const…

金融行業 UE/UI 設計:解鎖高效體驗,重塑行業界面

在數字化浪潮中&#xff0c;金融行業的競爭日益激烈&#xff0c;用戶體驗&#xff08;UE&#xff09;和用戶界面&#xff08;UI&#xff09;設計成為企業脫穎而出的關鍵。蘭亭妙微憑借豐富的經驗和創新的方法&#xff0c;為金融行業打造了一套行之有效的 UE/UI 解決方案&#x…

C語言字符函數,字符串函數以及內存函數

那么博主寫這一片博客的目的就是為下一篇c的string類做鋪墊&#xff0c;那么下面就請期待博主的下一篇文章吧。 目錄 1.字符函數 2.字符串函數&#xff08;均在string.h頭文件中&#xff09; strlen的使用和模擬實現 strcpy 的使用和模擬實現 strcat 的使用和模擬實現 s…

_DISPATCHER_HEADER結構中的WaitListHead和_KWAIT_BLOCK的關系

第一部分&#xff1a; // // Wait block // // begin_ntddk begin_wdm begin_nthal begin_ntifs begin_ntosp typedef struct _KWAIT_BLOCK { LIST_ENTRY WaitListEntry; struct _KTHREAD *RESTRICTED_POINTER Thread; PVOID Object; struct _KWAIT_BLOCK *R…

flutter 自定義控件RenderObjectWidget使用

CustomWidget的自定義組件的注釋還是比較清晰的 參考文檔Flutter實戰 import package:flutter/cupertino.dart; import package:flutter/gestures.dart; import package:flutter/material.dart; /* * 如果組件不會包含子組件&#xff0c;則我們可以直接繼承自 LeafRenderObject…