React基礎語法整理

安裝:
yarn create react-app reatc-lesson --template typescript
yarn create 創建一個react-app的應用 項目名稱 typescript 的模板

react-app 官方地址

https://create-react-app.bootcss.com/docs/adding-typescript

react 語法文檔

https://zh-hans.react.dev/learn#writing-markup-with-jsx

語法

基礎語法

組件函數

1、必須使用大駝峰命名;

2、return 之前可以定義組件使用的數據;

3、使用一對大括號即可使用定義對象的屬性;

4、reactNode 不支持直接渲染布爾值,布爾值用來條件渲染處理或者將其轉成字符串來使用。

5、reactNode 不支持直接渲染對象,需要將其轉為字符串才能直接渲染。

5、ReactNode 類型用來表示可以在React 組件中渲染的任何內容的一種類型,可以直接渲染:字符串、數字、元素或者或者包含這些類型的數組。

//return () 用來存放html組件的
function MyBanner(){//return 之前可以定義組件使用的數據const user = {name: 'Joe',}{/*  */} //注釋語法{/* 返回組件的根元素*/}return(<h1 className>Hello MyBanner {user.name}</h1>)(/* 也可以不使用括號返回 */)return <h1 className>Hello MyBanner {user.name}</h1>
}

注釋

語法: {/* */}

{/*  */} 

添加class名稱

只能使用小駝峰命名的屬性名:className 來指定一個css 的class,使用方式跟class一樣

<img className="avatar" />
.avatar {border-radius: 50%;
}

根據數據渲染視圖

將數據放在元素標簽中,放到一對大括號里,例如:{user.name}

return (<h1>{user.name}</h1>
)

還可以將定義的數據放到元素屬性上,但是必須使用大括號 而非引號。例如, className={avatar} 將avatar 字符串傳遞給 className,作為css的class,而非通過變量傳遞給className。但 src={user.imageUrl} 會讀取js 的 user.imageUrl 這個變量,然后將其讀取的值作為 src 屬性傳遞

return (
<img className='avatar' src={user.imageUrl} />
)

一個組件中返回多個元素

1、在React語法中,要去一個組件的返回值只能有一個根元素。

2、使用div 包裹多個元素是一種常見的方法,但有時會導致不必要的DOM層次結構。

解決:引入 <></> 作為一種更簡潔的方法。

<></> 是React 中的一種稱為 Fragment 的語法。它是一種用于在組件中返回多個元素而不需要創建額外DOM元素的簡潔方式

return (<><h1>{user.name}</h1><imgclassName="avatar"src={user.imageUrl}alt={'Photo of ' + user.name}style={{color:'red'width: user.imageSize,height: user.imageSize}}/></>);

添加style樣式

語法:style={{}} ,是style={} 大括號內的一個普通 {} 對象。當樣式依賴 js 變量時,可以使用 style屬性

return (<><h1>{user.name}</h1><imgclassName="avatar"src={user.imageUrl}alt={'Photo of ' + user.name}style={{color:'red'width: user.imageSize,height: user.imageSize}}/></>);

使用style樣式的方式

function MyComponent(){const styles = {color:'red',fontSize:'16px',fontWeight:'bold'}return (<div style={styles}>這是一個文本</div>;)
}{/* 使用css模塊化文件 */}
import styles from './styles.module.css';function MyComponent() {return (<div className={styles.myClass}>這是一個文本</div>)
}

條件渲染

React沒有特殊語句來編寫條件語句,使用的就是普通的 js 代碼。例如:if

  const user = {name: 'Joe',age: 32,isAdmin: false,isBanned: true,}let content;if(user.isAdmin){content = <h2>Welcome, {user.name}!</h2>}else {content = <h2>You are not an admin.</h2>}

或者通過組件:三目元算符

<div>{user.isBanned ? (<MyBanner />) : (<MyButton />)}</div>

又或者是 if 引入組件

let content;
if (isLoggedIn) {content = <MyBanner />
} else {content = <MyBanner2 />;
}
return (<div>{content}</div>
);

又或者是 &&

<div>{isLoggedIn && <AdminPanel />}
</div>

渲染列表

也是依賴js特性,例如for循環 和 map函數來渲染組件。

寫法1:組件外循環

const products = [{ title: 'Cabbage', id: 1 },{ title: 'Garlic', id: 2 },{ title: 'Apple', id: 3 },
]{/* 注意 li 里有一個key屬性。對應列表每一個元素,都應該傳遞一個字符串或數字給key,用于在其他 兄弟節點中唯一標識該元素,key是什么數據跟vue循環的key是一樣的 */}const listItem = user.products.map(item => <li key={item.id}>{item.title}</li>)<ul>{listItems}</ul>

寫法2,在組件內循環

<ul>{user.products.map((item,index) => (<li key={index}>{item.title}</li>))}
</ul>

寫法3:帶樣式

const itemList = user.products.map(item => <li key={item.id}style={{color: item.id % 2 === 0 ? 'red' : 'blue'}}>{item.title}</li>
)<ul>{itemList}
</ul>

for循環,沒其他寫法

const itemList2 = []for(let i = 0; i < user.products.length; i++){itemList2.push(<li key={i}>{user.products[i].title}</li>)}<ul>{itemList2}</ul>

響應事件

基本使用

也就是點擊事件咯,語法是 onClick={函數}

function App() {function handleClick (aaa :any){
{/* 在默認情況,事件監聽器的參數aaa 是一個事件對象(通常命名event,我現在命名aaa),這個事件包含事件類型、目標元素等 */}console.log('clicked',aaa) console.log(aaa.target) {/* 獲取目標元素*/}}return (<div className="App">{/* 基礎寫法1 */}<button onClick={handleClick}>點擊響應事件</button>{/* 內聯事件函數處理 */}<button onClick={function handleClick() {alert('hello')}}>OK1</button>{/* 簡潔箭頭函數 */}<button onClick={() => {alert('你點擊了我!');}}></div>);
}
事件監聽傳參

使用箭頭函數,在事件監聽中使用箭頭函數來傳遞參數。在箭頭函數中,可以訪問事件對象(入event)以及傳遞給事件監聽的其他參數

{/* 函數定義 */}
const handleClick2 =(aaa :Number) =>{console.log('clicked',aaa)return aaa
}或者function handleClick2 (aaa :Number){console.log('clicked',aaa)return aaa
}return (<button onClick={() => handleClick2(2)}>點擊傳值</button>
)

使用bind方法:

通過bind方法,可以綁定參數并創建一個新的函數,該函數將在事件觸發是被調用

{/* 函數定義 */}
const handleClick2 =(aaa :Number) =>{console.log('clicked',aaa)return aaa
}或者function handleClick2 (aaa :Number){console.log('clicked',aaa)return aaa
}return (<button onClick={handleClick2.bind(null,2)}>點擊傳值</button>
)
錯誤陷阱
錯誤1

傳遞事件處理函數的函數應該是直接傳遞,而非直接調用。

這個示例中,handleClick 作為一個 onClick 事件處理函數傳遞。這會讓React 記住,并且只在點擊按鈕的時候調用 傳遞的函數。

// 傳遞一個函數(正確寫法)
<button onClick={handleClick}></button>// 調用一個函數(錯誤寫法)
<button onClick={handleClick()}></button>
錯誤2
// 傳遞一個函數(正確)【alert 定義內聯事件函數,點擊的時候觸發】
<button onClick="{() => alert('...')}"></button>// 調用一個函數(錯誤)【這個 alert 在組件渲染時觸發,還不是在點擊時觸發】
<button onClick="{alert('...')}"></button>

其他常見響應事件

1、onChange 表單元素值發生變化觸發

當表單元素的值發生變化時觸發,比如輸入框的文本內容發生變化。

import React,{useState} from 'react';function App() {const [person, setPerson] = useState({name:'',age:0})function inputUpChange (event: React.ChangeEvent<HTMLInputElement>){const {name,value} = event.targetconsole.log(name,value) //打印 nanme屬性名,value 輸入值setPerson({...person, [name]: value}) //設置值}return (<div><h4>當前信息{JSON.stringify(person)}</h4><input type="text" name='name' value={person.name} onChange={inputUpChange} /><input type="text" name='age' value={person.age} onChange={inputUpChange} /></div>);
}
2、onSubmit 表單提交時觸發

點擊按鈕時觸發 form表單 提交函數 submitUserInfo

注意:在表單上使用 onSubmit 事件,并沒有阻止默認行為,它將觸發表單的默認提交行為,導致頁面刷新。

import React,{useState} from 'react';function App() {const [person, setPerson] = useState({name:'',age:0})function submitUserInfo(){console.log('submit',user)}return (<div><h4>當時onSubmit 信息</h4><form onSubmit={submitUserInfo}><input type="text" name='name' value={person.name} onChange={inputUpChange} /><button type='submit'>點擊觸發submit事件</button></form></div>);
}

為了阻止默認行為,可以在onSubmit 事件處理函數中調用 event.preventDefault() 方法。將阻止表單的默認提交行為,從而避免頁面刷新。

下面的代碼,在用戶點擊提交按鈕時候,submitHandle 函數將被調用,并且 e.preventDefault() 將阻止表單的默認提交行為,從而避免頁面刷新。可以在 submitHandle 函數中執行提交表單的邏輯。

在沒有使用 preventDefault 的情況下,打印的對象和數組無法展開的,因為在提交后表單的默認行為會導致刷新

import React,{useState} from 'react';const initialList = [{ id: 0, title: 'Big Bellies', seen: false },{ id: 1, title: 'Lunar Landscape', seen: false },{ id: 2, title: 'Terracotta Army', seen: true },
]
function StatesFormBox() {const [iibb,setIibb] = useState(initialList)function submitHandle(e: any){e.preventDefault()console.log(e)console.log(666,iibb)}function aa(){setIibb([{ id: 3, title: 'Terracotta Army', seen: false }])}return (<div>{/* <h1>State Form</h1> */}<form onSubmit={submitHandle}><button type='submit'>點擊按鈕提交</button></form></div>)
}export default StatesFormBox;
3、onMouseEnter 當鼠標移入元素時觸發
import React,{useState} from 'react';function App() {function handleMouseEnter(){console.log('鼠標移入元素了')}return (<div style={{width:'100px',height:'100px',border:'1px solid green'}} onMouseEnter={handleMouseEnter}><h4>鼠標進入</h4></div>);
}
4、onMouseLeave 當鼠標移出元素時觸發
import React,{useState} from 'react';function App() {function handleMouseLeave(){console.log('鼠標移出元素了')}return (<div style={{width:'100px',height:'100px',border:'1px solid green'}} onMouseLeave={handleMouseLeave}><h4>鼠標移出</h4></div>);
}
5、onKeyDown 當按下鍵盤上的任何信息時觸發

使用方法更上面類型

<input type="text" onKeyDown={handleKeyDown} />
6、onKeyUp 當釋放鍵盤上的任意鍵時觸發
<input type="text" onKeyUp={handleKeyUp} />
7、onFocus 當元素獲取焦點時觸發
<input type="text" onFocus={handleFocus} />
8、onBlur 當元素失去焦點時觸發
<input type="text" onBlur={handleBlur} />
9、onScroll 當前元素滾動時觸發
<div onScroll={handleScroll}>滾動時觸發</div>

子組件接收父組件的child,類型vue的v-text

import React,{useState} from 'react';
import Gallery from './Gallery';function App() {function clickHandle(num: number, num2: number){console.log(num + num2)return num + num2}function MyButton4({onClick, children}:{onClick:(num:number,num2:number)=>number,children:string}){return (<div>{/* 渲染會顯示 <button onClick={()=> onClick(2,2)}>我是傳遞</button> 的按鈕 */}<button onClick={()=> onClick(2,2)}>{children}</button></div>)}return (<div><MyButton4 onClick={clickHandle}>我是傳遞</MyButton4></div>);
}

更新界面

就是更改數據,更新視圖,數據驅動視圖

1、從useState 中獲得兩樣東西:當前的state(count),以及更新值的函數(setCount)。也可以起任何名字,但是慣例會像這樣:[something, setSomething] 這樣命名

2、第一次顯示,count 的值默認為0,因為你

import React from 'react';
import { useState } from 'react';
優化后一行搞到:
import React,{useState} from 'react';function App() {{/* 從useState 中獲得兩樣東西:當前的state(count),以及更新值的函數(setCount)。 */}const [count, setCount] = useState(0); {/* 默認值0*/}{/* 自定義命名 */}// 聲明一個num的狀態變量,并初始化為 2const [num, setNum] = useState(2); {/* 默認值2 */}function updateClick(){setCount(count + 1)}function updateNum(){setNum(num + 1)}return (<div className="App"><button onClick={updateClick}>點擊 {count} 了</button><button onClick={updateNum}>點擊了num值{num}了</button></div>);
}

Hook

再React中,以 use 開頭的函數都被稱為 Hook。 useState 是React 提供的內置 Hook 函數。

Hook 比普通函數更為嚴格。只能在組件(或者其他Hook)的頂層 調用 Hook。如果要在一個條件或者循環中使用 useState,需要在新的組件并在內部使用它。

注意

Hooks ---->以 use 開頭的函數 -----> 只能在組件活自定義 Hook的最頂部調用。 不能在條件語句、循環語句或者其他嵌套函數內調用 Hook。Hook是函數,但將其視為關于組件需求的無條件聲明。

//useState 的唯一參數是 state 變量的 初始值。在這個例子中,index的初始值被 useState(0) 設置為0
const [index, setIndex] = useState(0)
渲染步驟
  1. **組件進行第一次渲染。**因為你將 0 作為 index 的初始值傳遞給 useState ,它將返回 [0, setIndex] 。React 記住 0 是最新的 state值。
  2. 你更新了state。 當點擊按鈕時,調用 setIndex(index +1)index0 ,所以它是 setIndex(1) 。這告訴 React 現在記住 index1 并觸發下一次渲染。
  3. 組件進行第二次渲染。 React 仍然看到 useState(0), 但是因為 React 記住了你將 index 設置為了 1 ,它將返回 [1, setIndex]
  4. 以此類推

組件共享

在子組件里使用父組件傳的方法和變量數據

import React,{useState} from 'react';
import Gallery from './Gallery';// {person,size} 組件使用時的屬性,要一一對應,對接收的值類型驗證
function MyButton1({person,size}:{person:Object,size:number}) {return (<button>按鈕一號:{JSON.stringify(person)}---{size > 1 ? 1 : 2}</button>)
}
// onClick 是組件使用的屬性名,冒紅后面的對象是對這個函數的描述的類型解析
function MyButton2({onClick}:{onClick:() => void}) {return (<button onClick={onClick}>按鈕二{num}號</button>)
}
//接收一個屬性,對函數執行的時候參數和返回值的要去
function MyButton3({onClick}:{onClick:(num:number,num2:number)=>number}){return (<div><button onClick={()=> onClick(2,2)}>按鈕三</button></div>)
}function App() {const [num, setNum] = useState(2);return (<div><MyButton1 person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}size={100} /><MyButton2 onClick={updateNum} /><MyButton3 onClick={clickHandle} /></div>);
}

組件

定義組件

function Profile(){return (<imgsrc='https://www......'/>)
}

組件的導入導出

export 居然導出,export default 默認導出

function Profile(){return (<imgsrc='https://www......'/>)
}
// export 居然導出
export default function Gellery(){return (<h1>open111</h1><Profile />)
}

import 組件 from ‘組件文件地址’ =》 import Gallery from ‘./Gallery’

Gallery.tsx 導出
function Profile() {return (<imgsrc="https://i.imgur.com/QIrZWGIs.jpg"alt="Alan L. Hart"/>);}export default function Gallery(){return (<><h1>開始了</h1><Profile /><Profile /></>)}
app.tsx 導入

引入過程中,import Gallery from ‘./Gallery’; ,后綴.jsx添加與否都能正常使用。

import Gallery from './Gallery';function App() {return (<div><Gallery /><Gallery /></div>);
}
導入導出注意點

從一個文件中導出和導入多個文件

//用具名方式導出
export function Profile(){//****
}
//接著,具名導入的方式,從文件到當前組件文件中(用大括號)
import {Profile} from './Gallery.tsx'//渲染
export default function App() {return <Profile />;
}
//用默認導出的方式
export default function Gallery() {return (<section><h1>了不起的科學家們</h1></section>);
}// 導入 默認導出的組件
import Gallery from './Gallery.tsx';//渲染
export default function App() {return <Gallery />;
}

嵌套組組件

組件里可以渲染其他組件,但是 請不要嵌套定義組件的定義。下面這段代碼 非常慢,并且還會導致bug產生

export default function Gallery() {// 🔴 永遠不要在組件中定義組件function Profile() {// ...}// ...
}

正常使用

export default function Gallery() {// ...
}// ? 在頂層聲明組件
function Profile() {// ...
}

組件記憶:雙向綁定

組件通常需要通過 交互更改屏幕上顯示的呢絨。輸入表單 應該更新輸入字段,點擊輪播圖上的 “下一個”應該更改顯示的圖片,點擊 “購買” 應該將商品放入購物車。組件需要 “記住” 某些東西:當前輸入值、當前圖片、購物車等。值React中,這種組件特有的記憶稱為 state。

普通的變量的值改變時,更新變量的值時,組件沒有出現數據驅動視圖

普通變量無法驅動改變視圖

點擊按鈕,變量的值更新了,但是視圖沒有變化。

注意

updateAgeHandle() 事件處理函數整個更新局部變量 age,有兩個原因使得視圖沒有更新

1、**局部變量無法在多長渲染中持久保持。**當React 再次渲染這個組件時,會從頭開始渲染,不會考慮之前對局部變量的任何更改。

2、**更改局部變量不會觸發渲染。**React 沒有意識到它需要使用新數據再次渲染組件。

function App() {const user = {name: 'Joe',age: 32,}function updateAgeHandle(){user.age += 1console.log('age',user.age)}return (<div><h4>當前年齡:{user.age}</h4><button onClick={updateAgeHandle}>更新年齡</button></div>);
}
方案

要使用新數據更新組件,需要做兩件事

1、保留 渲染之間的數據。

2、觸發 React 使用新數據來渲染組件(重新渲染)

解決

useState Hook 提供了這兩個解決功能

1、State 變量 用于保存渲染間的數據。

2、State setter 函數 更新變量并觸發 React 再次渲染。

實現
// 要添加 state 變量,先從文件頂部導入 useState
import {useState} form 'react'// 然后 將局部變量定義的代碼換成state 變量
//替換后的 index 是一個state變量,setIndex 是對應的 setter 函數。
let index = 0;  【將其修改為】====>>>  const [index, setIndex] = useState(0) //初始變量0//函數中觸發
function updateAgeHandle(){setIndex(index + 1)
}

State 是隔離且私有的

State 是屏幕上組件實例內部的狀態。也就是說,**如果你渲染同一個組件兩次,每次副本都會有完全隔離的 state!**其中一個組件的state不會音響另外一個。

State 定義對象

看了這么多state 變量的定義,還是不太明白對象數據驅動視圖怎么弄。

如下實現 表單輸入更新

import React,{useState} from 'react';function App() {const [person, setPerson] = useState({name:'',age:0})function inputUpChange (event: React.ChangeEvent<HTMLInputElement>){const {name,value} = event.targetconsole.log(name,value) //打印 nanme屬性名,value 輸入值setPerson({...person, [name]: value}) //設置值}return (<div><h4>當前信息{JSON.stringify(person)}</h4><input type="text" name='name' value={person.name} onChange={inputUpChange} /><input type="text" name='age' value={person.age} onChange={inputUpChange} /></div>);
}

state 中更新數組

當操作 React state 中數組是時,你需要避免使用左列的方法,而首選右列的方法

避免使用 (會改變原始數組)推薦使用 (會返回一個新數組)
添加元素pushunshiftconcat[...arr] 展開語法(例子)
刪除元素popshiftsplicefilterslice(例子)
替換元素splicearr[i] = ... 賦值map(例子)
排序reversesort先將數組復制一份(例子)
添加元素
import React,{useState} from 'react'function ArrayDomState() {const [arr, setArr] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])function add1ArrHandle() {setArr([...arr,11])}function add2ArrHandle() {setArr(arr.concat(11))}return (<>{/* 添加元素 */}<div><p>array 數據:{JSON.stringify(arr)}</p><button onClick={add1ArrHandle}>state 擴展運算符[...arr] 添加 state 數組數據</button></div><div><p>array 數據:{JSON.stringify(arr)}</p><button onClick={add2ArrHandle}>使用 concat 添加 state 數組數據</button></div></>)
}export default ArrayDomState
刪除元素
import React,{useState} from 'react'function ArrayDomState() {const [arr, setArr] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])function delete1ArrHandle() {const newArr = arr.filter((item) => item !== 1)setArr(newArr)}function delete2ArrHandle() {const newArr = [...arr]newArr.splice(1, 1);setArr(newArr)}return (<>{/* 刪除元素 */}<div><p>arr 刪除元素{JSON.stringify(arr)}</p><button onClick={delete1ArrHandle}>state 使用 filter 刪除數組數據</button></div><div><p>arr 刪除元素{JSON.stringify(arr)}</p><button onClick={delete2ArrHandle}>state 使用 splice 刪除數組數據</button></div></>)
}export default ArrayDomState
轉換數組

這種方式就是轉換數組,就是使用 setState() 方法來更新組件的state,從而實現對數據的轉換操作。

const newArr = [...arr]
newArr.splice(1, 1);// 使用新的數組進行重渲染
setArr(newArr)
替換數組中的元素
import React,{useState} from 'react'function ArrayDomState() {const [arrs, setArrs] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])function arraysDomHandle() {const updateArr = arrs.map((item) => {if(item === 3){return 333333333} else {return item}});setArrs(updateArr)}return (<div><div><p>array 數據:{JSON.stringify(arrs)}</p><button onClick={arraysDomHandle}>點擊替換數組中的元素</button></div></div>)
}export default ArrayDomState
向數組中間插入元素

向數組特定位置插入一個元素,這個位置既不在數組開頭也不在數組末尾。

import React,{useState} from 'react'function ArrayDomState() const [arrs, setArrs] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])function insertArrHandle(){const insertIndex = 5;const newArr = [...arrs.slice(0, insertIndex),333333333,...arrs.slice(insertIndex)]setArrs(newArr)}return (<div>{/* 向數組中插入元素 */}<div><p>array 數據:{JSON.stringify(arrs)}</p><button onClick={insertArrHandle}>點擊向數組中插入元素</button></div></div>)
}export default ArrayDomState
其他更改數組的情況

總有些事情,是僅靠展開運算符和 map() 或者 filter() 等不會直接修改原值的方法能做到的。例如翻轉數組,或者數組排序,而 javaScript 中的 reverse() 和 sort() 方法會改變原數組,所以不能直接使用她們。

解決:先拷貝這個數組,然后再改變拷貝數組的值。

import React,{useState} from 'react'function ArrayDomState() const [arrs, setArrs] = useState([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])function changeArrHandle(){const newArr = [...arrs];newArr[0] = 333333333;newArr.reverse()setArrs(newArr)}return (<div>{/* 其他改變數組的情況 */}<div><p>array 數據:{JSON.stringify(arrs)}</p><button onClick={changeArrHandle}>點擊改變數組中的元素</button></div></div>)
}export default ArrayDomState
問題:

在上面的代碼中,雖然使用 [...arrs] 展開運算符創建了一份數組的拷貝值。當有了拷貝值后,就可以使用 newArr.reverse() 或者 newArr.sort() 這樣修改原數組的方法。 甚至可以通過 newArr[0] = 333333333 這樣的方式對特定元素進行賦值。

但是,這種拷貝方式,只能適用于 基礎類型的數組,不適用對象數組的元素。 原因大家應該都知道,這種解構的方式是淺拷貝,新數組種的對象依然與原始對象數組的原始的內存地址。因此,如果你修改了拷貝數組內部的某個對象。

//雖然 nextList 和 list 是兩個不同的數組,nextList[0] 和 list[0] 卻指向了同一個對象。因此,通過改變 nextList[0].name,list[0].name 的值也會被改變const nextList = [...list]
nextList[0].name = 'tom'
setList(nextList)
更新數組對象的元素
import React,{useState} from 'react'const initialList = [{ id: 0, title: 'Big Bellies', seen: false },{ id: 1, title: 'Lunar Landscape', seen: false },{ id: 2, title: 'Terracotta Army', seen: true },
]function ArrayDomState() {const [myList, setMyList] = useState(initialList)function handleToggleMyList(artworkId :number, nextSeen: any) {setMyList(myList.map((item) => {if(item.id === artworkId){// 創建包含變更的 新對象return {...item, title: nextSeen}}else {return item}}))}return (<div>{/* 更新數組內部對象的值 */}<div><p>更新數據{JSON.stringify(myList)}</p><button onClick={() => handleToggleMyList(1,'修改咯')}>更新對象數組</button></div></div>)
}export default ArrayDomState
使用Immer 編寫簡單的更新
import React,{useState} from 'react'
import { useImmer} from 'use-immer'const initialList = [{ id: 0, title: 'Big Bellies', seen: false },{ id: 1, title: 'Lunar Landscape', seen: false },{ id: 2, title: 'Terracotta Army', seen: true },
]function ArrayDomState() {const [myList2, setMyList2] = useImmer(initialList)function handleToggleMyList2(artworkId :number, nextSeen: any) {setMyList2(draft => {console.log('draft',JSON.stringify(draft))const artwork = draft.find((item) => item.id === artworkId) console.log('artwork', JSON.stringify(artwork))if(artwork) artwork.title = nextSeenconsole.log('artwork222',JSON.stringify(artwork))})}return (<div>{/* 使用Immer 編寫更加簡潔的更新邏輯 */}<div><p>使用 Immer 編寫:{JSON.stringify(myList2)}</p><button onClick={() => handleToggleMyList2(2,'Immer')}>使用Immer點擊</button></div></div>)
}export default ArrayDomState

想修改對象數組的值,還得先拷貝一份。

使用Immer 時,類似 artwork.seen = nextSeen 這種會產生 mutation的語法不會再有任何問題了:

updateMyTodos(draft => {const artwork = draft.find(a => a.id === artworkId);artwork.seen = nextSeen;
});

狀態管理

React 狀態管理是指在React 應用中有效地管理和共享組件之間的狀態。 React 也提供了一些內置的狀態管理:例如 使用組件本地的狀態( state) 和屬性 ( props ),以及使用上下文 ( context )進行狀態共享。

隨著應用不斷變大,應用變得更加復雜,這些內置的狀態管理機制可能會變得不夠靈活或難以維護。沉余或者重復的狀態往往是缺陷的根源。 為了解決這個問題,通常會使用 ReduxMobx 或者 React Context API

使用State 狀態相應輸入

在react種,不用直接從代碼層面上修改UI,不用編寫諸如 “禁用按鈕”、“啟用按鈕”、“顯示成功消息” 等命令。只需要描述組件在不同狀態(“初始狀態”、“輸入狀態”、“成功狀態”)下希望展示的UI,然后根據用戶輸入觸發狀態變更。

使用React 編寫的反饋表單,根據 status 這個狀態變量來決定顯示提交按鈕以及 是否顯示成功消息

import React,{useState} from 'react';
function StatesFormBox() {const [age, setAge] = useState('')const [error, setError] = useState('')const [status, setStatus] = useState('typind')if(status === 'success'){return <div>Success</div>}async function submitHandle(e: any){e.preventDefault()setStatus('loading')try {await submitForm(age)setStatus('success')} catch(error) {console.log(error)setStatus('typind')setError('error')}}function setAgeHandle(e:any){setAge(e.target.value)}return (<div>{/* <h1>State Form</h1> */}<div>{age}==={error}----{status}</div><form onSubmit={submitHandle}><textarea value={age} onChange={setAgeHandle}></textarea><button type='submit'>點擊按鈕提交</button></form></div>)
}function submitForm(age: string){return new Promise((resolve,reject) => {setTimeout(() => {console.log(age,age === '18')if(age !== '18') {reject(new Error('年齡不正確'))}else{resolve('提交成功')}},2000)})
}export default StatesFormBox;

在組件共享狀態

import React,{useState} from 'react';function Panel({title,children,isActive,onShow}:{title:string,children:string,isActive:boolean,onShow:()=>void}){return (<><h3>{title}</h3>{isActive ? (<p>children</p>) :(<button onClick={onShow}>顯示</button>)}</> )
}function StatesFormBoxShare() {const [activeIndex, setActiveIndex] = useState(0)function setActiveHandle(value:number){setActiveIndex(value)}return(<><Panel title='標題' isActive={activeIndex === 0} onShow={() => setActiveHandle(0)}>112313</Panel><Panel title='標題二' isActive={activeIndex === 1} onShow={() => setActiveHandle(1)}>22222</Panel></>)
}export default StatesFormBoxShare

useReducer 的使用

在hooks中提供了 useReducer 功能,可以增強 ReducerDemo 函數提供類似 Redux的的功能。

useReducer 能接受一個 reducer 函數 作為參數,reducer 接受兩個參數,一個是state 另外一個是action。然后返回一個狀態 count 和 dispath,count 是返回狀態中的值,而 dispath 是一個可以發布事件來更新 state 的。

基本使用
import React,{useReducer} from 'react'export default function ReducerDemo() {const [count, dispath] = useReducer((state,action)=> {//...}, 0);return (<div><h1 className="title">{count}</h1></div>)
}
要點

reducers 不應該包含異步請求、定時器、或者任何副作用(對組件外部有影響的操作),應該以不可變值的方式去更新對象和數組

修改對象

下面就是useReducer 更新的使用。

事件處理程序只通過派發 action 來 指定 發生了什么,而 reducer 函數通過 響應 actions 來決定 狀態如何更新

import React,{useReducer} from 'react'//(1)初始的數據
const initInfoData = {name : '張三',age : 18,sex : '男',
}//(2) 定義組件
function UseReducerBox (){//(3) useReducer 接受一個reducer參數:【reducerFun 自定義函數,自定義的這個reducer函數reducerFun 接受兩個參數:一個是state 另一個是action。】;useReducer 接受的第二個參數:【initInfoData 就是初始的 state 數據,就是初始數據】// useReducer 返回一個狀態 count:【userInfo】和 dispath:【setUserInfo】,userInfo 是返回狀態中的值,而 setUserInfo 是一個可以發布事件來觸發更新state的// count 和dispath 是官方示例的命名const [userInfo, setUserInfo] = useReducer(reducerFun,initInfoData)//(7) 點擊函數 觸發 發布事件來更新state 的。function handleClick(){//觸發發布更新后,useReducer 第一個參數就會執行了。setUserInfo('edit')}//(5)定義組件return (<><h5>useReducer 修改對象</h5><div>{JSON.stringify(userInfo)}</div>{/*(6) 觸發點擊*/}<button onClick={() => handleClick()}>點擊設置</button></>)
}//(3) 定義 useReducer 的第一個reducer參數,接收兩個參數 一個是 state 一個是action 
function reducerFun(state :any, action :any){//(4) state 當前狀態下的數據,action為接收 setUserInfo 這個更新state的參數console.log(state, action) // 打印:{name: '張三', age: 18, sex: '男'} 'edit'//這個if可以不用if(action === 'edit'){//返回修改后狀態數據return {name : '李四',age : 20,sex : '女'}}
}export default UseReducerBox
useState 和 useReducer 的對比
代碼體積

通常,在使用 useState 的時候,開始的時候只需要寫少量的代碼。 而 useReducer 必須提前編寫 reducer 函數和需要調度的 actions。 但是在多個事件處理程序以相似的方式修改 state 的時候, useReducer 可以減少代碼量。

可讀性:

狀態更新邏輯足夠簡單的時候 useState 的可讀性還可以,但是一旦邏輯變動復雜起來,就會使得代碼變得臃腫難以閱讀。這種情況下,useReducer 可以將狀態更新邏輯和 事件處理程序分離。

可調試性:

使用 useState 出現問題,必須單步執行更多代碼。而使用 useReducer 的時候,可以在 reducer 函數中通過打印日志的方式來觀察每個狀態的更新,以及為什么更新(來自哪個action)。如果所有的action都沒問題,就知道問題出在 reducer本身的邏輯了。

區分:

useState 是React 中 最簡單的狀態管理方法。使用簡單的對象來存儲狀態,并提供兩個方法來訪問和更新狀態

useReducer 提供了一種更加復雜的狀態管理方法。使用一個reducer 函數來處理狀態更新,并提供一個 dispatch() 方法來觸發狀態更新。

useState 關鍵區別在于如何處理狀態更新。 useState 使用簡單的對象來存儲狀態。這使得它很好使用,但也可能導致性能問題,因為每次狀態更新都會重新渲染組件。

useReducer 使用一個reducer函數 來處理狀態更新。這使得可以更有效地處理復雜的狀態更新,因為可以避免不必要的重新渲染。但是 useReducer 也更復雜,需要更多的學習和理解才能使用。

大多數情況下 useState 是足夠來管理簡單的狀態。但是,如果需要處理復雜的狀態更新,則 useReducer 可能是更好的選擇

實驗Immer 簡化 reducers

這與平常的 state 中 修改對象和數組一樣,可以使用Immer 庫來簡化 reducer。useImmerReducer 讓可以通過 push 或者 arr[ i ] = 來修改state

import React,{} from 'react'
import { useImmerReducer } from 'use-immer'const initInfoData = {name : '張三',age : 18,sex : '男',
}function ImmerReducerBox (){const [userInfo, setUserInfo] = useImmerReducer(reducerFun,initInfoData)function handleClick(obj: any){setUserInfo(obj)}return (<><h5>ImmerReducerBox 簡化 useReducer 對象</h5><div>{JSON.stringify(userInfo)}</div><button onClick={() => handleClick({type:'setName',payload:'大豆'})}>點擊設置姓名</button><button onClick={() => handleClick({type:'setAge',payload:'19'})}>點擊設置年齡</button><button onClick={() => handleClick({type:'setSex',payload:'女'})}>點擊設置性別</button></>)
}function reducerFun(draft: any,action :any){switch(action.type){case 'setName':draft.name = action.payloadbreakcase 'setAge':draft.age = action.payloadbreakcase 'setSex':draft.sex = action.payloadbreakdefault:break}}export default ImmerReducerBox

使用 Context 深層次傳遞參數

描述

通常使用 props 將信息從父組件傳遞到子組件。但是,如果必須通過許多中間件向下傳遞props,或者在應用中的許多組件需要相同信息,傳遞props 會變得十分冗長和不變。Context 允許父組件向其下層無論多深的任何組件提供信息,無需通過props 顯示傳遞

props 傳遞帶來的問題

props 傳遞 是將數據通過 UI 樹顯式傳遞到 子組件的好方法。

但是當需要在組件樹深層遞參數以及需要在組件間復用相同的參數時,傳遞 props 就會變得很麻煩。最近的根節點的父組件可能離需要的組件很遠,狀態提升 到太高的層級會導致 逐層傳遞 props 的情況

在這里插入圖片描述

Context的基本使用
import React,{createContext,useContext} from 'react'const initInfoData = {name : '張三',age : 18,sex : '男',
}
function Childrens() {return (<><div>第一個子元素</div><Childrens2 /></>)
}
function Childrens2() {const aaa = useContext(conTextS)return (<div>第二個子元素{JSON.stringify(aaa)}</div>)
}const conTextS = createContext(initInfoData)
function ContextBox (){return (<><div>1232222</div><Childrens /></>)
}export default ContextBox

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

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

相關文章

Vue筆記(一)基礎

VUE 官方文檔&#xff1a;https://cn.vuejs.org/ 創建VUE項目 前提&#xff1a;已安裝 16.0 或更高版本的 Node.js 進入要創建的目錄&#xff0c;執行命令&#xff1a;npm create vuelatest 安裝依賴&#xff0c;啟動&#xff1a; //進入項目目錄&#xff0c;運行命令安裝依賴…

基于Vue框架的電子商城購物平臺小程序的設計與開發

基于JavaWebSSMVue電子商城購物平臺小程序系統的設計和實現 源碼獲取入口KaiTi 報告/Ren務書Lun文目錄前言主要技術系統設計功能截圖訂閱經典源碼專欄Java項目精品實戰案例《500套》 源碼獲取 源碼獲取入口 KaiTi 報告/Ren務書 一、選題的目的和意義 自從微信推出了微信小程序…

使用命令行移除VSAN中故障磁盤

原創作者&#xff1a;運維工程師 謝晉 使用命令行移除VSAN中故障磁盤 前提故障盤移除 前提 客戶有套VSAN環境內有一臺服務器的磁盤組出現了一塊故障的數據盤&#xff0c;但該盤已經處于完全掉線狀態&#xff0c;無法進行正常移除。如下圖&#xff1a; 如果遇到這種情況&am…

P9 LinuxC 進程概述 終端啟動的程序父進程是終端

前言 &#x1f3ac; 個人主頁&#xff1a;ChenPi &#x1f43b;推薦專欄1: 《C_ChenPi的博客-CSDN博客》??? &#x1f525; 推薦專欄2: 《Linux C應用編程&#xff08;概念類&#xff09;_ChenPi的博客-CSDN博客》??? &#x1f6f8;推薦專欄3: ??????《鏈表_ChenP…

【1】一文讀懂PyQt簡介和環境搭建

目錄 1. PyQt簡介 1.1. Qt 1.2. PyQt 1.3. 關于PyQt和PySide 2. 通過pip安裝PyQt5 3. 無法運行處理 4. VSCode配置PYQT插件 PyQt官網:Riverbank Computing | Introduction 1. PyQt簡介 PyQt是一套Python的GUI開發框架,即圖形用戶界面開發框架。 Python中經常使用的GU…

FreeRTOS的內存管理方法(超詳細)

內存管理 我們知道每次創建任務、隊列、互斥鎖、軟件定時器、信號量或事件組時&#xff0c;RTOS 內核都需要 RAM &#xff0c; RAM 可以從 RTOS API 對象創建函數內的 RTOS 堆自動動態分配&#xff0c; 或者由應用程序編寫者提供。 如果 RTOS 對象是動態創建的&#xff0c;那么…

Leetcode—2646.最小化旅行的價格總和【困難】

2023每日刷題&#xff08;五十三&#xff09; Leetcode—2646.最小化旅行的價格總和 算法思想 看靈神的 實現代碼 class Solution { public:int minimumTotalPrice(int n, vector<vector<int>>& edges, vector<int>& price, vector<vector&l…

發現數學之美--微積分的起源和用途(一文搞懂微積分)

數學&#xff0c;改變世界的基石。微積分十九世紀的三大自然發現之一&#xff0c;迪卡爾建立了解析幾何&#xff0c;把數與圖結合在一起&#xff0c;微積分的發現與創立&#xff0c;是數學新的里程碑&#xff0c;解決了常規方法無法解決的問題&#xff0c;是一次偉大的革命。迪…

服務器數據損壞了有辦法修復嗎 ?

對于企業網站來說&#xff0c;數據庫往往是服務器中最核心的部分&#xff0c;所以一旦數據庫發生損壞&#xff0c;將會給企業帶來巨大的損失&#xff0c;因 此數據庫的數據恢復功能變得越來越重要了。在服務器運行過程中&#xff0c;由于斷電、操作不當或者是客觀原因損壞到服務…

git安裝和配置

git安裝和配置 一、軟件介紹 Git是一個免費開源的分布式版本控制系統&#xff0c;旨在快速高效地處理從小型到大型項目的所有內容。 Git易于學習&#xff0c;占地面積小&#xff0c;性能閃電般快。它以廉價的本地分支、方便的暫存區域和多個工作流等功能勝過了Subversion、C…

linux 常用指令目錄大綱

Linux下的Signal信號處理及詳解&#xff0c;test ok-CSDN博客 Linux下怎樣判斷一個binary是否可以debug//test ok_感知算法工程師的博客-CSDN博客 linux file命令的用法//test ok-CSDN博客 linux下生成core dump方法與gdb解析core dump文件//test ok-CSDN博客 linux readel…

【論文閱讀】Reachability and distance queries via 2-hop labels

Cohen E, Halperin E, Kaplan H, et al. Reachability and distance queries via 2-hop labels[J]. SIAM Journal on Computing, 2003, 32(5): 1338-1355. Abstract 圖中的可達性和距離查詢是許多應用的基礎&#xff0c;從地理導航系統到互聯網路由。其中一些應用程序涉及到巨…

第7節:Vue3 動態綁定多個屬性

可以使用v-bind指令將多個屬性動態綁定到元素上。以下是一個簡單的實例&#xff1a; <template><view class"container"><text v-bind"dynamicProps">{{ message }}</text><button click"toggleActive">切換激活…

金南瓜SECS/GEM C# SDK 快速使用指南

本文對如何使用金南瓜SECS/GEM C# SDK 快速創建一個滿足SECS/GEM通信要求的應用程序&#xff0c;只需簡單3步完成。 第一步&#xff1a;創建C# .NET程序 示例使用Visual Studio 2010&#xff0c;使用者可以選擇更高級版本 Visual Studio 第二步&#xff1a;添加DLL庫引用&am…

圖論-并查集

并查集(Union-find Sets)是一種非常精巧而實用的數據結構,它主要用于處理一些不相交集合的合并問題.一些常見的用途有求連通子圖,求最小生成樹Kruskal算法和最近公共祖先(LCA)等. 并查集的基本操作主要有: .1.初始化 2.查詢find 3.合并union 一般我們都會采用路徑壓縮 這樣…

git標簽的管理與思考

git 標簽管理 git 如何打標簽呢&#xff1f; 標簽是什么? 標簽 相當于一個 版本管理的一個貼紙&#xff0c;隨時 可以通過標簽 切換到 這個版本的狀態 &#xff0c; 有人可能有疑問 git commit 就可以知道 代碼的改動了&#xff0c; 為啥還需要標簽來管理呢&#xff1f; …

從二分類到多分類:探索Logistic回歸到Softmax回歸的演進

隨著機器學習和深度學習的迅猛發展&#xff0c;我們需要越來越靈活和強大的模型來解決各種不同的問題。在分類問題中&#xff0c;Logistic回歸一直是一個常見而有效的工具&#xff0c;尤其是在二分類場景中。然而&#xff0c;隨著問題變得更加復雜&#xff0c;我們需要更先進的…

node筆記

文章目錄 一、Node.js基礎1. 認識Node.js01 nodejs的特性02 使用 Node.js 需要了解多少 JavaScript03 瀏覽器環境vs node環境 2. 開發環境搭建3. 模塊、包、commonJS02 CommonJS規范03 modules模塊化規范寫法 4. Npm&Yarn01 npm的使用02 全局安裝 nrm03 yarn使用 5. 內置模…

在idea中使用maven創建dynamic web project

1、先創建一個empty project 2、添加一個module , 核心是選擇maven archetype webapp, 這個是maven提供的創建web工程的模版。 3、添加完等自動安裝好即可 4、目錄可能不完整 右鍵src---->點擊New---->點擊Directory &#xff08;注意&#xff1a;這是筆者所缺失的結…

每日一道c語言

任務描述 題目描述:輸入10個互不相同的整數并保存在數組中&#xff0c;找到該最大元素并刪除它&#xff0c;輸出刪除后的數組 相關知識&#xff08;略&#xff09; 編程要求 請仔細閱讀右側代碼&#xff0c;結合相關知識&#xff0c;在Begin-End區域內進行代碼補充&#xf…