react 學習筆記 李立超老師 | (學習中~)

文章目錄

  • react學習筆記01
    • 入門
      • 概述
      • React 基礎案例HelloWorld
        • 三個API介紹
      • JSX
        • JSX 解構數組
      • 創建react項目(手動)
      • 創建React項目(自動) | create-react-app
      • 事件處理
      • React中的CSS樣式
        • 內聯樣式 | 內聯樣式中使用state (不建議使用)
        • 外部樣式表 | CSS Module
    • React組件
      • 函數式組件和類組件
        • 生成一組標簽/組件
      • props 父組件給子組件傳屬性/方法
        • 給組件設置className樣式不生效
      • state 維護組件的響應式狀態
        • useState(stateInitValue)
      • Ref 獲取DOM對象
      • 非受控組件與受控組件
        • 數據的雙向綁定
        • 子組件給父組件傳值 = props傳遞函數 + 子組件調用函數
        • vue中v-if與v-show的React寫法
        • Portal 將元素渲染到指定位置
      • Fragment 組件
      • Context 祖先組件向子孫組件傳值
    • Effect 副作用
      • setState()在函數組件中的執行流程
      • React.StrictMode

react學習筆記01

學習視頻 react18 李立超

學習中get到的新用法

  1. Date類的toLocalString方法,可以更為靈活的處理Date類。

  2. 標簽屬性中閉包的使用
    舉例:僅在刪除狀態時使用id,不需要單獨傳遞id屬性。

    const logItemDate = logsData.map(item=> <LogItem  onDelLog ={()=> delLog(item.id)}>)
    
  3. 移動端適配 rem + vw
    可以使用vw獲取視口寬度,將font-size設置單位為vw,然后結合rem做適配。
    1vw = 視口寬度的1% -> 100vw = 視口的寬度

    一般設置html的font-size值 = 屏幕寬度/設計稿寬度,但移動端比如375px計算出的font-size值小于12px會造成一些錯誤和奇怪的問題,因此把比例擴大100倍

    為了使比例不變,相應的設計圖元素使用時設計圖元素大小/100 rem

    根html的font-size值 = 屏幕寬度/設計稿寬度*100 
    font-size = 100vw/設計稿寬度*100
    

入門

概述

AJAX+DOM可以實現網頁的局部刷新,但是新數據不能直接在網頁中顯示,需要通過DOM將數據轉換為網頁中的節點。

react幫助我們根據不同的數據來快速構建用戶項目,同時在構建過程中確保其流暢度。

react特點

1.使用虛擬DOM而不是真正的DOM

2.聲明式編碼(聲明式:結果為導向,不關心結果 命令式:一行代碼一個命令)

3.支持服務器端渲染

React 基礎案例HelloWorld

入門案例采用外部引入腳本使用(正常開發使用包管理器)

  • react.development.js reactreact核心庫,只要使用react就必須要引入。下載地址
  • react-dom.development.js react-domreactdom包,使用react開發web應用時必須引入。下載地址
  • babel.min.js 瀏覽器不能識別JSX,利用該babelJSX轉換為JS代碼。下載地址

1.引入腳本

<script src="../script/react.development.js"></script>
<script src="../script/react-dom.development.js"></script>

2.創建一個React元素

React.createElement(組件名/元素名,元素中的屬性,元素的子元素/內容)

const reactDiv = React.createElement('div',{},'我是react創建的div'); 

3.獲取根元素對應的React元素

ReactDOM.createRoot(Dom元素);

// html
<div id="root"></div>
// js
const root = ReactDOM.createRoot(document.getElementById('root'));

4.將reactDiv渲染到React根元素中

root.render(reactDiv)
三個API介紹
  • React.createElement(type,[props],[...children]) 用來創建React元素(并不是ReactDom,所以這里使用React調用)

    • class屬性需要使用className屬性代替。

    • type如果是標簽名(元素)需要全小寫,首寫母大寫會被認為是組件

    • 在設置屬性時,事件名應遵守駝峰命名法,事件值需要是一個函數,不能是console.log(xx)這種表達式。如果直接寫一個函數調用語句,則在綁定事件時就會被調用(之后事件不會被觸發)

    • React元素是一次性的,一旦創建就無法修改,只能使用新創建的元素進行替代

  • ReactDOM.createRoot(container[,options]);用來創建React的根容器,根容器用來放置React元素

    • 將參數的DOM元素轉換為React根元素
  • ReactDOM實例.render(ReactElement)React元素渲染到根元素中

    • DOM根元素中所有的內容都會被刪除(不會修改DOM根元素本身),被React元素轉換而成的DOM元素替換
    • 重復調用render(),React會將兩次虛擬DOM進行對比,確保只修改發生變化的元素,對DOM做最少修改。首次調用時,容器節點里的所有DOM都會被替換,后續的調用則會使用ReactDOM差分算法(diff)進行更新

JSX

上述方法中React.createElement('button', {}, '我是按鈕')還是命令式編碼方法,告訴reactcreateElement去創建一個button按鈕,該按鈕沒有屬性,內容為我是按鈕。

聲明式編程結果導向,告訴結果,不關系過程怎么樣。

const button = <button>我是按鈕</button>; // 告訴react我需要一個button按鈕元素,不關心react如何創建

React中可以通過JSXJavaScript Syntax Extension)來創建React元素,JSX 讓我們以類似于HTML 的形式去使用 JSJSXReact中聲明式編程的體現方式。

JSX需要被翻譯為JS代碼,才能被React執行。 要在React中使用JSX,必須引入babel來完成“翻譯”工作。

  • JSX就是React.createElement()的語法糖,最終都會轉換為以調用React.createElement()創建元素的代碼。

  • JSX在執行之前都會被babel轉換為JS代碼

    <!-- 引入babel -->
    <script src="script/babel.min.js"></script>
    <!--設置js代碼被babel處理-->
    <script type="text/babel">const div = <div>我是一個div<button>我是按鈕</button></div>;const root = ReactDOM.createRoot(document.getElementById('root'));root.render(div);    
    </script>
    
  • JSX不是字符串,不需要加引號

const div =  <div>我是一個div</div>  // 正確寫法   
  • JSXhtml標簽應該小寫開頭,React組件應該大寫開頭
<div> // 小寫html標簽
<Div> // 大寫組件
  • JSX有且只有一個根標簽

  • JSX的標簽必須正常結束(自結束標簽必須寫/)

const input = <input type="text" / >
  • JSX中使用{}嵌入表達式(有值的語句就是表達式)
const name = "ranran"
const div = <div>{name}</div> // 才會顯示ranran,沒有括號會把name識別為字符串
  • 如果表達式值為空值、布爾值、undefined,將不會顯示

  • JSX屬性可以直接在標簽中設置

    • 事件綁定需要是一個函數,而不能直接是函數調用(綁定時就會被觸發,不會延遲觸發)
    • className代替class
    • style必須使用對象設置,屬性名必須用駝峰命名法
const div = <div onClick="()=>{console.log('ranran')}" style={{backgroundColor: "yellowgreen", border: '10px red solid'}}
></div> // 外面的大括號表示style必須使用對象設置,里面的對象表示給他設置的值是一個對象(有多個樣式)
  • 在語句中可以操作JSX

    const name = 'ranran';
    const lang = 'cn';let div;
    if(lang === 'en'){div = <div>hello {name}</div>;
    }else if(lang === 'cn'){div = <div>你好 {name}</div>;
    }
    const root = ReactDOM.createRoot(document.getElementById('root'))
    root.render(div)
    
JSX 解構數組

JSX在解構{}的內容時,如果內容是數組則會自動將其展開。

//頁面:孫悟空豬八戒沙和尚
const data = ['孫悟空', '豬八戒', '沙和尚'];
const div = <div>{data}</div> 
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(div)/*
· 孫悟空
· 豬八戒
· 沙和尚
*/
const data = ['孫悟空', '豬八戒', '沙和尚'];
const list = <ul>{data.map(item => <li>{item}</li>)}</ul>;
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(list)//頁面:孫悟空豬八戒沙和尚
const data = ['孫悟空', '豬八戒', '沙和尚'];
const div = <div>{data}</div> 
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(div)/*
· 孫悟空
· 豬八戒
· 沙和尚
*/
const data = ['孫悟空', '豬八戒', '沙和尚'];
const list = <ul>{data.map(item => <li>{item}</li>)}</ul>;
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(list)

React通過虛擬DOMReact元素和原生DOM元素進行映射

當我們調用root.render時。頁面就會發生重新渲染
React通過diff算法將新的虛擬DOM和舊的比較,找到發生變化的元素,并且只對變化的元素進行修改。

數組中(當前數組)每一個元素都需要設置一個唯一key
重新渲染頁面時,Reactkey值會比較key值相同的元素,沒key值會按照順序進行比較。

  1. 開發中一般會采用數據的 id 作為 key
  2. 盡量不使用元素的 index 作為 key 索引會跟著元素順序的改變而改變,所以使用索引做 key 跟沒有 key 是一樣的。 唯一的不同就是,控制臺的警告沒了。 當元素的順序不會發生變化時,用索引做 key 也沒有什么問題。
const data = ['孫悟空', '豬八戒', '沙和尚'];
const list = <ul>{data.map(item => <li key={ item }>{ item }</li>)}</ul>;
// const list = <ul>{data.map((item,index) => <li key={ index }>{ item }</li>)}</ul>;
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(list)

?

創建react項目(手動)

React官方為了方便開發,提供react-scripts包(①打包②測試服務器-根據代碼變化自動刷新避免改一點就重新打包),包中提供了項目開發中的大部分依賴。
由于提供了配置好的工具,我們一些操作就要符合約定。

使用包管理器管理項目,沒有辦法直接放在網頁中運行。需要經過webpack打包,才能在瀏覽器中正常執行。

  1. 創建React
根目錄- public(可以web直接訪問的文件,不用打包就可以瀏覽器訪問的靜態資源)- index.html (入口文件,必須有,首頁模板打包時以此為模板生成最終的index/html | 添加標簽 <div id="root"></div>- src(源碼,JS源代碼)- index.js(必須,webpack打包文件的入口,該文件會被自動引入public/index.html中)
  1. pnpm init 初始化項目,生成package.json文件(大部分時候這一步可以省略)
  2. pnpm install react react-dom react-scripts 安裝項目依賴
  3. 編寫代碼src/index.js
// 引入ReactDOM
import ReactDOM from 'react-dom/client';// 創建一個JSX
const APP = <div><h1>這是一個react項目</h1></div>// 獲取一個根元素
const root = ReactDOM.createRoot(document.getElementById('root'));
// 將APP渲染進根容器
root.render(APP);
  1. 運行項目
  • pnpm react-scripts build 打包項目,一般開發完成之后需要上線時使用該命令進行打包。
    初次需要輸入y確認。打包時需要默認配置,會詢問是否添加默認配置。

在這里插入圖片描述

正常情況,右鍵打開會報錯。因為打包好的文件需要部署在服務器上運行,而不是直接使用瀏覽器打開。每次打包后路徑都是這樣需要手動修改。

在這里插入圖片描述

  • pnpm react-scripts start 開發中使用的命令
    通過webpack啟動內部的測試服務器,可以實時對更新代碼進行編譯。這個命令太長,可以在package.jsonscripts 選項中配置命令,下次可以使用命令pnpm start

    "scripts": {"start": "react-scripts start"
    }
    

react 一定需要兩個文件

  • public/index.html:入口文件,首頁模板打包時以此為模板生成最終的index/html - 提供dom root根節點
  • src/index.jswebpack打包文件的入口,該文件會被自動引入public/index.html中 - 將root轉化為react根節點元素后,將react元素掛載到react根節點中

創建React項目(自動) | create-react-app

命令:npx create-react-app 項目名

除了public/index.htmlsrc/index.js必須保留外,其他的東西都是可以刪除的。

/*reate-react-app 創建index.js其中<React.StrictMode>使用嚴格模式渲染React元素 - 可以不使用
*/
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode>aaa</React.StrictMode>
);

事件處理

react 元素的事件處理和 DOM 元素的很相似,但是有一點語法上的不同:

  • React 事件的命名采用小駝峰式(camelCase),而不是純小寫。

  • 使用 JSX 語法時需要傳入一個函數作為事件處理函數。事件綁定需要是一個函數,而不能直接是函數調用(綁定時就會被觸發,不會延遲觸發,等于將函數的返回值給了該事件)

    // 傳統 HTML
    <button onclick="activateLasers()">Activate Lasers
    </button>
    // React
    <button onClick={activateLasers}>  Activate Lasers
    </button>
    
  • React事件通過會傳遞事件對象event,但其不同于原生的事件對象,是React包裝后的事件對象,該對象已經處理了跨瀏覽器的兼容性問題。

    React中事件回調函數不能通過返回false阻止默認行為,必須顯式地使用event事件對象的preventDefault方法

    // 傳統 HTML<form οnsubmit="console.log('You clicked submit.'); return false"><button type="submit">Submit</button>
    </form>// React
    function Form() {function handleSubmit(e) {e.preventDefault();    console.log('You clicked submit.');}return (<form onSubmit={handleSubmit}><button type="submit">Submit</button></form>);
    }
    

React中的CSS樣式

內聯樣式 | 內聯樣式中使用state (不建議使用)

style必須使用對象設置,屬性名必須用駝峰命名法

const StyleDemo = () => {return (<div style={{color:'red', backgroundColor:'#bfa', fontSize:20, borderRadius:12}}>我是Div</div>);
};export default StyleDemo;

當樣式過多,JSX會比較混亂,可以使用變量去保存對象

import React from 'react';const StyleDemo = () => {const divStyle = {color: 'red', backgroundColor: '#bfa', fontSize: 20, borderRadius: 12}return (<div style={divStyle}>我是Div</div>);
};export default StyleDemo;

內聯樣式中使用state
當樣式是動態時,可以在樣式中使用state變量。

import React, {useState} from 'react';const StyleDemo = () => {const [showBorder, setShowBorder] = useState(false);const divStyle = {color: 'red',backgroundColor: '#bfa',fontSize: 20,borderRadius: 12,border: showBorder?'2px red solid':'none'};const toggleBorderHandler = ()=> {setShowBorder(prevState => !prevState);};return (<div style={divStyle}>我是Div<button onClick={toggleBorderHandler}>切換邊框</button></div>);
};export default StyleDemo;
外部樣式表 | CSS Module

外部樣式是指將樣式編寫到外部的css文件中,直接通過import引用。

直接import引入的樣式都是全局樣式,其他組件也看得見這個樣式。如果不同的樣式表中出現了相同的類名,會出現相互覆蓋情況。

import './index.css'

CSS Module
使用CSS Module后,網頁中元素的類名會自動計算生成并確保唯一

如果引用同一個模塊,計算出來的類名是相同的。

CSS ModuleReact中已經默認支持(前提是使用了react-script)

  1. 文件樣式的文件名為xxx.module.css
  2. 在組件中引入樣式的格式為import xxx from './xxx.module.css'
  3. 設置類名時通過xxx.yyy的形式來設置
/*
StyleDemo.module.css
*/
.myDiv{color: red;background-color: #bfa;font-size: 20px;border-radius: 12px;
}/*
StyleDemo.js
*/
import Styles from './StyleDemo.module.css';const StyleDemo = () => {return (<div className={Styles.myDiv}>我是Div</div>);
};export default StyleDemo;

React組件

組件需要遵守的規則

  • 組件名首字母必須大小(小寫字母開頭的組件會被視為原生DOM標簽)
  • 組件中只能有一個根元素

函數式組件和類組件

React中定義組件有兩種方式

  • 基于函數的組件 - 函數式組件(推薦) :函數組件是返回JSX普通函數
  • 基于類的組件 - 類組件

函數式組件

函數組件是返回JSX普通函數

//1.創建函數式組件 App.js
const App = () => {return <div>我是App組件!</div>
};
// 2.導出App
export default App;// index.js
// 3.引入App
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById('root'));
// 4.React組件可以直接通過JSX渲染
root.render(<App/>);	//root.render(App()); 也可以,只是<App/>內部做了更多的事情。

類組件

1.創建一個ES6 class,并繼承于React.Component

2.添加一個render方法,方法的返回值為JSX

import React from "react"
//1.創建類組件  必須要繼承React.Component
class App extends React.Component{constructor(props){ // 參數props接受父組件的傳值this.state = 'xxx' //state的使用}// 2.添加render方法render(){return <div>我是一個類組件{this.props}</div>}
}// index.js
// 3.引入App
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById('root'));
// 4.React組件可以直接通過JSX渲染
root.render(<App/>);	//root.render(App()); 也可以,只是<App/>內部做了更多的事情。

props、state、ref

  • 類組件的props存儲在類的實例對象中,可以通過this.props訪問。

  • 類組件中state統一存儲到了實例對象的state屬性中,可以通過this.state來訪問,通過this.setState()修改。

    • 通過this.setState修改state,只修改設置了state的屬性,并不會修改沒設置的第一層屬性。
  • 通過React.createRef()(函數式為useRef)創建屬性存儲DOM對象,同樣通過對象.current獲取

  • 事件回調函數需要定義為類的方法,建議使用箭頭函數,這樣this指向的是react實例。否則函數里的this會執行設置事件的dom元素

import React,{ Component } from 'react'class App extends Component{state = {count:0,age:{} }  divRef = React.createRef();// 事件回調函數需要定義為類的方法,建議使用箭頭函數,這樣this指向的是react實例。否則函數里的this會執行設置事件的dom元素clickHandler = ()=>{// 寫法1:this.setState({count:this.state.count+1})// 寫法2this.setState(prevCount => {return {count:prevCount+1;}})}retnder(){return <div><h1 ref={ divRef }>this.props<h1><h1 @onClick={this.clickHandler}>this.state.count<h1>   </div>}
}
生成一組標簽/組件

react中對于根據數組數據產生一組標簽或者一組組件,沒有類似vuev-for指令,一般使用{ data.map(JSX) }的語法進行生成。

const App= () => {const data = [{title:"1",id:"0"},{title:"2",id:"1"},{title:"3",id:"2"}];return <div>{ data.map(item => <Button key={item.id} titile={item.title}></Button >) }</div> /*寫法1 return <div> { data.map(item => <Button key={item.id} titile={item.title} />) }  </div> 寫法2:將對象的每個屬性都傳遞return <div> { data.map(item => <Button {...item}>) </div>}*/
};export default App;

props 父組件給子組件傳屬性/方法

父組件通過使用子組件時,定義自定義參數傳遞屬性/方法。子組件通過參數props接收(函數式組件的第一個參數)。
react中的props類似vue中的props是只讀屬性是無法修改的

props.children中可以獲取到父組件中,子組件標簽體內部的值。

// 父組件
<Button bgColor='red' color={ color }>我是一個按鈕</Button>//子組件
const Button = (props) => {return <button style={{backgroundColor:props.bgColor, color:props.color}}>{props.children}</button>;
};export default Button;
給組件設置className樣式不生效

原因

className會被認為是一個屬性傳遞給子組件,需要在子組件的根元素使用className={props.className}接收。

state 維護組件的響應式狀態

React中,當組件渲染完畢后,再修改組件中的變量,不會使組件重新渲染。state相當于一個變量,只不過在React中進行了注冊。React會監控整個變量的變化,當state發生變化時,會自動觸發組件的重新渲染。

頁面的渲染靠的是render函數

state概述

stateprops類似,都是一種存儲屬性的方式。

  • state只屬于當前組件(組件的私有屬性),其他組件無法使用。
  • state的值是對象,當其內容發生變化相關組件會一起刷新
useState(stateInitValue)

通過鉤子函數useState(stateInitValue)創建stateReact中鉤子函數只能用于函數組件或自定義鉤子。

  • 參數是整個state變量的初始值
  • 函數返回一個數組[stateVariable,setStateFunction],第一個元素是state變量的初始值(只用于顯示),第二個元素是修改該變量的函數(函數的參數為新值)。調用修改函數修改state變量的值(state值發生變化)會觸發組件的重新渲染,直接修改state變量不會觸發組件的重新渲染。
import { useState } from 'React'const [stateVariable,setStateFunction] = useState(1);

注意點

  1. state值是一個對象時,setState()修改時,使用新的對象去替換已有對象。
const [user, setUser] = useState({name:"ranran",age:18})
user.name = "xxx"; 
serUser(user); // user是對象,對象的地址沒有發生變化,所以不會引起組件重新渲染/* 
解決方案:將其拷貝給另一個新對象,修改新對象的屬性
*/
setUser({...user,name:"xxx"}) // 后面的name會覆蓋前面的name
  1. 通過setState()去修改一個state時,并不表示修改當前的state,修改的是組件下一次渲染的state

  2. setState()會觸發組件的異步渲染(并不是馬上調用就渲染,放入事件循環隊列中等待執行),所以當調用setState()需要使用state值時,可能出現計算錯誤。

    因為setState()修改的是下一次渲染的state,如果下一次渲染還沒進行前又調用了setState(),此時state還是舊值,所以就會出現計算錯誤。

    解決辦法 : 通過傳遞回調函數的形式修改state

    回調函數的返回值會成為新的state值,回調函數執行時React會將最新的state值作為參數傳遞。

    setCount(state => state+1); // 傳遞參數,React會保證參數的state是最新值
    

如果setState()中需要用到舊值,參數都采用函數的形式。

Ref 獲取DOM對象

Refreference的簡寫,用來獲取真實DOM的引用。

  • 使用useRef()鉤子函數獲取DOM對象
    • 1.通過useRef()鉤子函數返回一個普通JS對象,React會自動將DOM對象傳遞到該對象的current屬性中。
    • 2.被引用的DOM元素上添加ref屬性,值為上述的對象。
      根據描述,直接創建一個有current屬性的普通JS對象可以實現相同的效果。

兩種方法的不同點

  • 自定義對象方法,組件每次重新渲染,都會創建一個新對象
  • 使用useRef()函數返回的對象的聲明周期和組件的聲明周期一致,所以每次重新渲染,該ref對象都是原來的。
import {useRef} from 'react';const MyComponent = () => {const divRef = useRef();/*const divRef = {current:null}*/const clickHandler = () => {console.log(divRef);};return (<div ref={divRef} onClick={clickHandler}>一個div</div>      );
};export default MyComponent;

非受控組件與受控組件

非受控組件:表單中的數據來源于用戶填寫的組件,表單元素的值不會更新state,輸入數據都是現用現取的。

受控組件:使 Reactstate 成為唯一數據源,由state控制表單。

數據的雙向綁定

將表單的value綁定為state數據,表單的onChange事件觸發時,通過事件對象event獲取到新值,然后使用setState修改state的值為新值。

import { useState } from 'react';
import './index.css';const Demo = () => {// 如果有多個表單,可以將表單數據設置為一個對象const [inputValue, setInputValue] = useState('');return (<><inputtype="text"className="inputDemo"value={inputValue}onChange={e => {setInputValue(e.target.value);}}/></>);
};export default Demo;
子組件給父組件傳值 = props傳遞函數 + 子組件調用函數
  1. 在父組件中,使用props給子組件傳遞一個自定義事件
  2. 在子組件中將需要傳遞的數據作為函數參數,調用函數
// 父組件
<LogsItem onSavaLog={ savaLogHandler }>// 子組件
const LogsItem = (props) => {props.savaLogHandler("需要傳遞的數據");
}

關于傳遞setState函數給子組件的一些說法:盡量不要這樣做,state在哪里,setState盡量就在哪里。

vue中v-if與v-show的React寫法
  • v-if-v-else配對出現 可以使用條件判斷
  • v-show/僅有v-if 可以使用&&
// v-if/v-else 可以使用條件判斷
控制變量 ? v-if顯示的 : v-else顯示的// v-show/僅有v-if 可以使用&& 
控制變量 && v-show顯示的

如果顯示出來的組件內部需要修改外部的控制變量,react中一般的做法時將函數作為參數傳遞。因為控制變量在外部,內部只需要調用該函數,外部修改控制變量的值。

Portal 將元素渲染到指定位置

React中,父組件引入子組件后,子組件會直接在父組件內部渲染。換句話說,React元素中的子組件,在DOM中,也會是其父組件對應DOM的后代元素。

問題描述
每個組件都是相同的構成(想象成一個列表),組件內部包含一個子組件,該子組件的作用是生成一個遮罩覆蓋全局。
組件1開啟相對定位,遮罩開啟固定定位(不一定是和這個例子相同的定位方式,這里舉例)
由于組件1組件2組件3的 z-index:1,后面的組件會覆蓋前面的。所以組件1中的遮罩出現時,覆蓋不了組件2組件3,即使遮罩的z-index:999(理解為在組件1內部元素的層級中占比很高,但不影響組件1的層級),但組件1和其他兄弟組件層級相同(父元素組件1都被覆蓋了子元素肯定被一起覆蓋)。


結構問題:遮罩需要遮住視圖不應該作為組件123的子組件,如果必須這樣寫,解決辦法是使用Portal 將組件渲染到指定位置

ReactDOM.createPortal(需要渲染的元素,傳送到的指定位置):渲染元素時將元素渲染到網頁中的指定位置
1.在index.html中添加一個新的元素

<div id="root"></div>
<!--這個容器用來專門渲染遮罩層-->
<div id="backdrop"></div>

2.在組件中通過ReactDOM.createPortal()將元素渲染到新建的元素中

const backdropDOM = document.getElementById('backdrop');// 在其他組件內部正常使用Backdrop組件,但是該組件渲染時會被傳送到專門渲染遮罩層的容器中渲染,會脫離原來的結構
const Backdrop = () => {return ReactDOM.createPortal(<div>{props.children}</div>,backdropDOM);
};

Fragment 組件

React中,JSX必須有且只有一個根元素,這導致在某些情況需要添加一個額外的父元素(并沒有實際意義)

  • React提供了Fragment組件,Fragment可以讓你聚合一個子元素列表,并且不在DOM中增加額外節點
  • <></>Fragment的語法糖,<></> 語法不能接受鍵值或屬性,但Fragment可以傳遞 key 屬性
import React from 'react';const MyComponent = () => {return (<React.Fragment><div>我是組件1</div><div>我是組件2</div><div>我是組件3</div></React.Fragment>/*<><div>我是組件1</div><div>我是組件2</div><div>我是組件3</div></>*/);
};export default MyComponent;

Context 祖先組件向子孫組件傳值

Context相當于一個公共的存儲空間

創建content

// defaultValue存儲的值
export const MyContext = React.createContext({name:xxx,age:xxx,
});

訪問到Context中的數據

  • 方式1:通過Consumer標簽來訪問到Context中的數據(不常用)

    該組件內部必須使用函數,解析時會調用該函數,將創建的defaultValue作為該函數的參數傳遞。

    import React from 'react';
    import { MyContext } from '../store/test-context';const MyContext = () => {return (<MyContext.Consumer>{(ctx)=>{ // 上述案例中的defaultValuereturn (<ul><li>{ctx.name}</li><li>{ctx.age}</li></ul>);}}</MyContext.Consumer>);
    };
    export default MyComponent;
    
  • 方式2:使用鉤子函數useContext(context參數)獲取到context,該鉤子函數會返回Context中的數據

    import React, {useContext} from 'react';
    import { MyContext } from '../store/test-context';const MyComponent = () => {const ctx = useContext(MyContext);return (<ul><li>{ctx.name}</li><li>{ctx.age}</li></ul>);
    };export default MyComponent;
    

? 一般不會將數據直接放在Context,因為這樣寫是死數據并且與state響應式數據沒什么關系,不會觸發組件的重新渲染。所以React還提供了Provider組件,用于在數據所在的組件中指定Context值。

import React from "react";
import MyComponent from "./component/MyComponent";
import { MyContext } from "./store/test-context";// 數據所在的組件
const App = () => {// 指定context的值return <MyContext.Provider value={{name:'豬八戒', age:28}}>/* Provider的子組件 */<MyComponent/>   </MyComponent.Provider>;
};export default App;

Provider設置在外層組件中,通過value屬性來指定Context的值。這個Context值在所有的Provider子組件中都可以訪問。Context的搜索流程類似vueprovide inject

Effect 副作用

組件每次重新渲染,組件的函數體就會執行。

有一部分邏輯如果直接寫在函數體中,會影響到組件的渲染,這部分會產生“副作用”的代碼,是不能直接寫在函數體中。

例如,如果直接將修改state的邏輯編寫到了組件之中,每次函數體執行設置基礎值,state變量又引起組件的更新,就會導致組件不斷的循環渲染,直至調用次數過多內存溢出。

setState()在函數組件中的執行流程

setState()會調用dispatchSetDate()方法,dispatchSetDate()方法的主要邏輯

  • 判斷組件當前處于什么階段(渲染階段 |非渲染階段 )

    • 處于渲染階段:不會檢查state值是否相同,在此時直接將setState設置的值放入渲染隊列等待渲染

    • 處于非渲染階段:檢查setState設置的值與之前的值是否相同。如果值不同,對組件進行重新渲染;如果值相同,則不對組件進行重新渲染。

處于渲染階段案例

const App = () => {const [count,setCount] = uesState(0);// 會觸發Too many re-renders報錯 // 調用的時候處于渲染階段,因為div沒有渲染到頁面上,所以會引發重新渲染,再次調用組件函數。也就是說無限循環,不會退出渲染階段。setCount(0); return (<div>{count}</div>)
}

處于非渲染階段案例

第一次點擊按鈕count = 0 -> 1,組件重新渲染。

第二次點擊按鈕count = 1 -> 1,組件重新渲染。

第三次點擊按鈕count = 1 -> 1,組件沒有重新渲染。

這是因為當值相同時,React在某些情況下(通常發生在值第一次相同時)會繼續執行當前組件的渲染(這里指的時組件函數執行并更新頁面),這次渲染不會產生實際效果(這里應該僅重新執行組件函數并不更新頁面,不觸發刷新沒有什么用??)并且不會觸發子組件的渲染。

const App = () => {const [count,setCount] = uesState(0);const clickHandler = ()=>{setCount(1); }return (<div onClick={ clickHandler }>{count}</div>)
}

React.StrictMode

腳手架自動生成的index.jx中使用了該組件,該組件表示react自身開啟嚴格模式,開啟后react會自動去檢查組件中是否有副作用的代碼(并不是很智能)。

root.render(<React.StrictMode><App/></React.StrictMode>
)

React的嚴格模式,在開發模式下,會主動重復調用一些函數,以使副作用出現。這些函數會被調用兩次,如果安裝了React Developer Tool,調試作用的第二次調用會顯示為黑色。

  • 類組件的 constructor, render, 和 shouldComponentUpdate 方法
  • 類組件的靜態方法 getDerivedStateFromProps
  • 函數組件的函數體
  • 參數為函數的setState
  • 參數為函數的useState, useMemo, useReducer

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

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

相關文章

【數據結構和算法】反轉字符串中的單詞

其他系列文章導航 Java基礎合集數據結構與算法合集 設計模式合集 多線程合集 分布式合集 ES合集 文章目錄 其他系列文章導航 文章目錄 前言 一、題目描述 二、題解 2.1 方法一&#xff1a;雙指針 2.2 方法二&#xff1a;分割 倒序 三、代碼 3.1 方法一&#xff1a;雙…

不同品牌的手機如何投屏到蘋果MacBook?例如小米、華為怎樣投屏比較好?

習慣使用apple全家桶的人當然知道蘋果手機或iPad可以直接用airplay投屏到MacBook。 但工作和生活的多個場合里&#xff0c;并不是所有人都喜歡用同一品牌的設備&#xff0c;如果同事或同學其他品牌的手機需要投屏到MacBook&#xff0c;有什么方法可以快捷實現&#xff1f; 首先…

1 億個數據取出最大前 100 個有什么方法?

1 億個數據取出最大前 100 個有什么方法&#xff1f; 大家好&#xff0c;這是一道經常在面試中被遇到的一個問題&#xff0c;我之前面試也是被問到過得&#xff0c;現在一起學習下&#xff0c;下次再被問到就可以輕松地用對。 在計算機科學和數據處理領域&#xff0c;我們經常…

【GDB】

GDB 1. GDB調試器1.1 前言1.2 GDB編譯程序1.3 啟動GDB1.4 載入被調試程序1.5 查看源碼1.6 運行程序1.7 斷點設置1.7.1 通過行號設置斷點1.7.2 通過函數名設置斷點1.7.3 通過條件設置斷點1.7.4 查看斷點信息1.7.5 刪除斷點 1.8 單步調試1.9 2. GDB調試core文件2.1 設定core文件的…

(五)五種最新算法(SWO、COA、LSO、GRO、LO)求解無人機路徑規劃MATLAB

一、五種算法&#xff08;SWO、COA、LSO、GRO、LO&#xff09;簡介 1、蜘蛛蜂優化算法SWO 蜘蛛蜂優化算法&#xff08;Spider wasp optimizer&#xff0c;SWO&#xff09;由Mohamed Abdel-Basset等人于2023年提出&#xff0c;該算法模型雌性蜘蛛蜂的狩獵、筑巢和交配行為&…

iOS(swiftui)——系統懸浮窗( 可在其他應用上顯示,可實時更新內容)

因為ios系統對權限的限制是比較嚴格的,ios系統本身是不支持全局懸浮窗(可在其他app上顯示)。在iphone14及之后的iPhone機型中提供了一個叫 靈動島的功能,可以在手機上方可以添加一個懸浮窗顯示內容并實時更新,但這個功能有很多局限性 如:需要iPhone14及之后的機型且系統…

Java面試遇到的一些常見題

目錄 1. Java語言有幾種基本類型&#xff0c;分別是什么&#xff1f; 整數類型&#xff08;Integer Types&#xff09;&#xff1a; 浮點類型&#xff08;Floating-Point Types&#xff09;&#xff1a; 字符類型&#xff08;Character Type&#xff09;&#xff1a; 布爾類…

(六)五種最新算法(SWO、COA、LSO、GRO、LO)求解無人機路徑規劃MATLAB

一、五種算法&#xff08;SWO、COA、LSO、GRO、LO&#xff09;簡介 1、蜘蛛蜂優化算法SWO 蜘蛛蜂優化算法&#xff08;Spider wasp optimizer&#xff0c;SWO&#xff09;由Mohamed Abdel-Basset等人于2023年提出&#xff0c;該算法模型雌性蜘蛛蜂的狩獵、筑巢和交配行為&…

【完整項目】雙模式答題卡識別軟件中YOLO模式的訓練部分詳解,包括訓練填涂區域和手寫準考證號,手把手詳細教學,可延申拓展訓練其他圖像數據

目錄 前言1. 數據準備2. 數據標注3. 先跑起來Windows下用本地的CPU或GPU訓練本地Windows系統連接服務器訓練前言 前文:【完整項目】基于Python+Tkinter+OpenCV+Yolo+手寫OCR的雙模式答題卡識別軟件的設計與實現 如果你需要訓練自己的答題卡模型,那么請先看上面的文章鏈接。…

Flutter自定義下拉選擇框dropDownMenu

利用PopupMenuButton和PopupMenuItem寫了個下拉選擇框&#xff0c;之所以不采用系統的&#xff0c;是因為自定義的更能適配項目需求&#xff0c;話不多說&#xff0c;直接看效果 下面直接貼出代碼、代碼中注釋寫的都很清楚&#xff0c;使用起來應該很方便&#xff0c;如果有任何…

C : DS靜態查找之順序索引查找

Description 給出一個隊列和要查找的數值&#xff0c;找出數值在隊列中的位置&#xff0c;隊列位置從1開始 要求使用順序索引查找算法&#xff0c;其中索引表查找和塊內查找都采用不帶哨兵、從頭開始的順序查找方法。 Input 第一行輸入n&#xff0c;表示主表有n個數據 第二…

OpenSSL 編程指南

目錄 前言初始化SSL庫創建SSL 上下文接口(SSL_CTX)安裝證書和私鑰加載證書(客戶端/服務端證書)加載私鑰/公鑰加載CA證書設置對端證書驗證例1 SSL服務端安裝證書例2 客戶端安裝證書創建和安裝SSL結構建立TCP/IP連接客戶端創建socket服務端創建連接創建SSL結構中的BIOSSL握手服務…

Scrum

Scrum是一個用于開發和維持復雜產品的框架&#xff0c;是一個增量的、迭代的開發過程。在這個框架中&#xff0c;整個開發過程由若干個短的迭代周期組成&#xff0c;一個短的迭代周期稱為一個Sprint&#xff0c;每個Sprint的建議長度是2到4周(互聯網產品研發可以使用1周的Sprin…

【Linux】輸出緩沖區和fflush刷新緩沖區

目錄 一、輸出緩沖區 1.1 輸出緩沖區的使用 1.2 緩沖區的刷新 1.3 輸出緩沖區的作用 二、回車換行 一、輸出緩沖區 C/C語言&#xff0c;當調用輸出函數&#xff08;如printf()、puts()、fwrite()等&#xff09;時&#xff0c;會給我們提供默認的緩沖區。這些數據先存…

虛擬機安裝 hyper—v 沙盒

一、下載系統鏡像 1、確認電腦內存在8G及以上并提前準備完整的系統鏡像 安裝Hyper-V并重啟電腦后打開程序選擇虛擬機 選擇安裝位置并設置保留第一代的虛擬參數即可開始分配內存&#xff0c;根據自己的需求進行設置 右鍵虛擬機啟動并開始運行&#xff0c;進行鏡像系統的安裝便完…

【Flutter】創建應用頂級組件,應用根組件 (學習記錄)

前言 在 Flutter 中&#xff0c;應用的頂級組件或根組件通常是在 main() 函數中通過 runApp() 方法創建的。這個組件通常是一個 MaterialApp、CupertinoApp、GetMaterialApp 或其他類似的應用框架組件。 以下是一個創建 MaterialApp 作為根組件的示例&#xff1a; void main()…

牛客算法心得——環形數組的連續子數組最大和(dp)

大家好&#xff0c;我是晴天學長&#xff0c; 一個找連續子數組最大和的變形題&#xff0c;需要的小伙伴可以關注支持一下哦&#xff01;后續會繼續更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .環形數組的連續子數組的最大和 描述 給定一個長度為 nn 的環形整數數組&…

『 MySQL數據庫 』聚合統計

文章目錄 前言 &#x1f951;&#x1f95d; 聚合函數&#x1f353; COUNT( ) 查詢數據數量&#x1f353; SUM( ) 查詢數據總和&#x1f353; AVG( ) 查詢數據平均值&#x1f353; MAX( ) 查詢數據最大值&#x1f353; MIN( ) 查詢數據最小值 &#x1f95d; 數據分組GROUP BY子句…

湖科大計網:計算機網絡概述

一、計算機網絡的性能指標 一、速率 有時候數據量也認為是以10為底的&#xff0c;看怎么好算。&#xff08;具體吉大考試用什么待商榷&#xff09; 二、帶寬 在模擬信號系統中帶寬的含義&#xff0c;本課程中用到的地方是&#xff1a;香農定理和奈奎斯特定理公式的應用之中。 …

全面高壓化與全面超快充,破解新能源汽車的時代難題

是什么讓新能源車主感到疲憊與焦慮&#xff1f;是什么阻擋更多消費者選擇新能源汽車&#xff1f;我們在身邊進行一個簡單的調查就會發現&#xff0c;問題的答案非常一致&#xff1a;充電。 充電難&#xff0c;充電慢的難題&#xff0c;始終是困擾新能源汽車產業發展&#xff0c…