React state狀態(tài)屬性詳細(xì)講解
1. 基本使用
要點(diǎn):
- 成員屬性 state 它是一個(gè)特殊的屬性,它是當(dāng)前類的私有數(shù)據(jù),只有在當(dāng)前的組件中才能操作里面的數(shù)據(jù)
- 狀態(tài)( state )即數(shù)據(jù),是組件內(nèi)部的私有數(shù)據(jù),只能在組件內(nèi)部使用,和vue中data差不多,不過它沒有像vue中的data進(jìn)行了數(shù)據(jù)劫持
- state的值是對(duì)象,表示一個(gè)組件中可以有多個(gè)數(shù)據(jù)
- 通過this.state來獲取狀態(tài),react 中沒有做數(shù)據(jù)代理
- 想要修改 state 數(shù)據(jù)值同時(shí)讓視圖更新則需要調(diào)用專用的方法 this.setState
- state 它是類的一個(gè)成員屬性
- state 中的數(shù)據(jù)可讀可寫的
使用:
import React, { Component } from 'react';
class App extends Component {
// 初始化方式1
// state = {
// num: 100
// }
// 初始化方式2
constructor(props) {
super(props);
this.state = {
num: 100
}
}
addNum(evt, n = 1) {
// 同步修改數(shù)據(jù),它不會(huì)觸發(fā)視圖更新
this.state.num += n
this.forceUpdate()
console.log(this.state.num);
}
render() {
return (
<div>
{/* 在視圖中讀取state中的數(shù)據(jù) */}
<h3>{this.state.num}</h3>
<button onClick={evt => this.addNum(evt, 2)}>累加</button>
</div>
);
}
}
export default App;
2. 使用setState操作state數(shù)據(jù)
概述:
修改 state 中的 num 屬性數(shù)據(jù)的同時(shí),讓視圖更新,用到專門用來修改 state 中數(shù)據(jù)的方法 setState。
語法:
# 語法1
this.setState({
key:value
})
# 語法2 推薦
this.setState(state => {
return {key:value}
})
使用:
import React, { Component } from 'react';
class App extends Component {
state = {
num: 100
}
addNum(evt, n = 1) {
// 對(duì)象方式更新 批處理 => 數(shù)組【隊(duì)列】 虛擬dom比對(duì)
// 它是一個(gè)異步操作
this.setState({
num: this.state.num + 1
})
console.log(this.state.num);
// this.setState({
// num: this.state.num + 1
// }, () => {
// // 回調(diào)函數(shù)中就可以得到最新的狀態(tài)數(shù)據(jù)
// console.log(this.state.num);
// })
}
render() {
return (
<div>
{/* 在視圖中讀取state中的數(shù)據(jù) */}
<h3>{this.state.num}</h3>
<button onClick={evt => this.addNum(evt, 2)}>累加</button>
</div>
);
}
}
export default App;
注意:
注意執(zhí)行結(jié)果中,雖然視圖發(fā)生了更新,但是控制臺(tái)打印 state 中的數(shù)據(jù)并沒有發(fā)生改變,說明這里的更新數(shù)據(jù)是異步操作。
之所以是異步操作,是因?yàn)?React 在這里做了批處理。即當(dāng)執(zhí)行多次修改數(shù)據(jù)的操作時(shí),React 并不會(huì)立即執(zhí)行,而是將這些操作存入一個(gè)異步隊(duì)列中,然后再進(jìn)行統(tǒng)一處理,這樣做可以提升性能。假如我現(xiàn)在要修改 3 次數(shù)據(jù),不用批處理需要更新視圖 3 次,而如果進(jìn)行批處理的話,只需要更新視圖一次。更新視圖需要進(jìn)行 dom 比對(duì),只比 1 次顯然比比對(duì) 3 次性能更好。
關(guān)于 setState 方法(React 批處理)中的對(duì)象合并:
import React, { Component } from 'react';
class App extends Component {
state = {
num: 100
}
addNum(evt, n = 1) {
this.setState({
num: this.state.num + 1
})
this.setState({
num: this.state.num + 2
})
this.setState({
num: this.state.num + 3
})
console.log(this.state.num);
// 函數(shù),它不會(huì)合并,它會(huì)依次執(zhí)行 == 建議多用函數(shù)方式,保證數(shù)據(jù)完整性
// this.setState(state => ({
// num: state.num + 1
// }))
// this.setState(state => ({
// num: state.num + 2
// }))
// this.setState(state => ({
// num: state.num + 3
// }))
// console.log(this.state.num);//6
}
render() {
return (
<div>
{/* 在視圖中讀取state中的數(shù)據(jù) */}
<h3>{this.state.num}</h3>
<button onClick={evt => this.addNum(evt, 2)}>累加</button>
</div>
);
}
}
export default App;
從圖中運(yùn)行結(jié)果可以看出,當(dāng)我們多次修改數(shù)據(jù)時(shí),視圖只針對(duì)最后一次修改作出渲染。這是因?yàn)樵?React 批處理中(批處理隊(duì)列是一個(gè)集合,這里看作是一個(gè)對(duì)象),會(huì)進(jìn)行對(duì)象合并。什么意思呢?首先對(duì)象的 key 值是唯一的,當(dāng) key 值 num 已經(jīng)存在時(shí),再傳 num 會(huì)對(duì)對(duì)象中已經(jīng)存在的 num 進(jìn)行修改,即對(duì)象中有效的 num 的值是最后一次操作:this.state.num + 3。
補(bǔ)充說明:批處理隊(duì)列的底層進(jìn)行的是[...obj1,...obj2]的操作,所以重復(fù)的項(xiàng)只會(huì)出現(xiàn)一次。而函數(shù)方式的批處理進(jìn)行的是[fn1,fn2...],所以會(huì)依次執(zhí)行。這里的解釋過于牽強(qiáng)和抽象,只是為了簡(jiǎn)單理解和記憶,更加完整的闡述需要了解底層之后,再加深理解。
關(guān)于 setState 方法的同步操作:
react17 及之前版本,如果你把當(dāng)前的操作不寫在合成事件中,則 setState 它就是同步的,react18全是異步的。
setState 在執(zhí)行的過程中,它會(huì)判斷當(dāng)?shù)膱?zhí)行環(huán)境,如果為宏任務(wù)等,則它會(huì)立即執(zhí)行。
也就是說, setState 方法只要寫在合成事件處理方法中,它就是異步的;只要不寫在合成事件中,它都是同步。(僅限于 react17 及之前版本)
比如 setState 方法寫在宏任務(wù)中或者寫在原生事件中就是同步的。
例如下面這樣的寫法就是同步操作:
import React, { Component } from 'react';
class App extends Component {
// setState它是同步的也是異步的
// 只要寫在合成事件處理方法中,它就是異步
// 只要不寫在合成事件中,它都是同步
state = {
num: 100
}
addNum(evt, n = 1) {
setTimeout(() => {
// 寫在宏任務(wù)中的setState它是同步的
this.setState({
num: this.state.num + 3
})
console.log(this.state.num);
}, 0);
}
// 類似于vue中的mounted方法
// componentDidMount() {
// // 寫在原生事件中它的setState也是同步的
// // document.onclick = () => {
// // this.setState({
// // num: this.state.num + 3
// // })
// // console.log(this.state.num);
// // }
//
render() {
return (
<div>
{/* 在視圖中讀取state中的數(shù)據(jù) */}
<h3>{this.state.num}</h3>
<button onClick={evt => this.addNum(evt, 2)}>累加</button>
</div>
);
}
}
export default App;
3. 案例-toDoList
import React, { Component } from 'react';
class App extends Component {
state = {
todos: []
}
onEnter = evt => {
if (evt.keyCode === 13) {
let title = evt.target.value.trim()
// 方案1
// let todos = this.state.todos.concat({ id: Date.now(), title, done: false })
// this.setState({ todos })
// 方案2
// this.state.todos.push({ id: Date.now(), title, done: false })
// this.forceUpdate() // 這里也可以寫成這一句:this.setState({})
// 如果你setState寫了一個(gè)空對(duì)象,則它會(huì)更新視圖一次
// this.setState({})
// 如果你寫了為null,setState不會(huì)做任何事件,相當(dāng)于沒有調(diào)用
// this.setState(null)
// 方案3:更新數(shù)據(jù),推薦,確保數(shù)據(jù)的完整性
this.setState(state => ({
todos: [...state.todos, { id: Date.now(), title, done: false }]
}), () => evt.target.value = '')
}
}
del = tid => () => {
this.setState(state => ({
todos: state.todos.filter(({ id }) => id != tid)
}))
}
delIndex = index => () => {
this.state.todos.splice(index, 1)
this.setState({})
}
render() {
return (
<div>
<div>
<input type="text" onKeyUp={this.onEnter} />
</div>
<hr />
<ul>
{
this.state.todos.map((item, index) => (
<li key={item.id}>
<span>{item.title}</span>
{/* 按 id 刪除 */}
<span onClick={this.del(item.id)}>刪除</span>
{/* 按索引刪除 */}
<span onClick={this.delIndex(index)}>刪除</span>
</li>
))
}
</ul>
</div>
);
}
}
export default App;
注意:
setState 還有兩種擴(kuò)展用法:
如果你setState寫了一個(gè)空對(duì)象,則它會(huì)更新視圖一次
this.setState({})
如果你寫了為null,setState不會(huì)做任何事件,相當(dāng)于沒有調(diào)用
this.setState(null)
4. 案例-購物車
import React, { Component } from 'react';
class App extends Component {
state = {
carts: [
{ id: 1, name: '水果手機(jī)14', price: 1, num: 1 },
{ id: 2, name: '大米手機(jī)14', price: 1, num: 2 },
{ id: 3, name: '一般手機(jī)14', price: 1, num: 3 },
]
}
setNum = (n, index) => {
// 方案1
this.state.carts[index]['num'] += n
if (this.state.carts[index]['num'] <= 1) this.state.carts[index]['num'] = 1
if (this.state.carts[index]['num'] >= 5) this.state.carts[index]['num'] = 5
this.setState({})
// 方案2
// this.setState(state => {
// state.carts[index]['num'] += n
// let carts = state.carts
// return { carts }// 同等于 return {carts:carts}
// })
}
totalPrice = () => {
return this.state.carts.reduce((p, c) => {
p += c.num * c.price
return p
}, 0)
}
render() {
return (
<div>
<table width='600' border='1'>
<thead>
<tr>
<th>ID</th>
<th>名稱</th>
<th>價(jià)格</th>
<th>數(shù)量</th>
</tr>
</thead>
<tbody>
{
this.state.carts.map((item, index) => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.name}</td>
<td>{item.price}</td>
<td>
<button onClick={() => this.setNum(-1, index)}>---</button>
<span>{item.num}</span>
<button onClick={() => this.setNum(1, index)}>+++</button>
</td>
</tr>
))
}
</tbody>
</table>
<hr />
<h3>{this.totalPrice()}</h3>
</div>
);
}
}
export default App;
到此這篇關(guān)于React state狀態(tài)屬性詳細(xì)講解的文章就介紹到這了,更多相關(guān)React state狀態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React實(shí)現(xiàn)數(shù)字滾動(dòng)組件numbers-scroll的示例詳解
數(shù)字滾動(dòng)組件,也可以叫數(shù)字輪播組件,這個(gè)名字一聽就是非常普通常見的組件。本文將利用React實(shí)現(xiàn)這一組件,感興趣的小伙伴可以了解一下2023-03-03
React?Hooks--useEffect代替常用生命周期函數(shù)方式
這篇文章主要介紹了React?Hooks--useEffect代替常用生命周期函數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
一文詳解React Redux設(shè)計(jì)思想與工作原理
最近看項(xiàng)目中使用了?Redux,?便嘗試了解一波?Redux?的設(shè)計(jì)思想與工作原理,所以本文詳細(xì)的給大家介紹了Redux設(shè)計(jì)思想與工作原理,需要的朋友可以參考下2023-09-09
如何使用React構(gòu)建一個(gè)高效的視頻上傳組件
本文介紹了如何使用React構(gòu)建一個(gè)高效的視頻上傳組件,包括基礎(chǔ)概念、常見問題與解決方案以及易錯(cuò)點(diǎn),通過實(shí)際代碼案例,展示了如何實(shí)現(xiàn)文件大小和格式驗(yàn)證、上傳進(jìn)度顯示等功能,并詳細(xì)解釋了跨域請(qǐng)求和并發(fā)上傳控制等技術(shù)細(xì)節(jié)2025-01-01

