React state狀態(tài)屬性詳細(xì)講解
1. 基本使用
要點(diǎn):
- 成員屬性 state 它是一個(gè)特殊的屬性,它是當(dāng)前類(lèi)的私有數(shù)據(jù),只有在當(dāng)前的組件中才能操作里面的數(shù)據(jù)
- 狀態(tài)( state )即數(shù)據(jù),是組件內(nèi)部的私有數(shù)據(jù),只能在組件內(nèi)部使用,和vue中data差不多,不過(guò)它沒(méi)有像vue中的data進(jìn)行了數(shù)據(jù)劫持
- state的值是對(duì)象,表示一個(gè)組件中可以有多個(gè)數(shù)據(jù)
- 通過(guò)this.state來(lái)獲取狀態(tài),react 中沒(méi)有做數(shù)據(jù)代理
- 想要修改 state 數(shù)據(jù)值同時(shí)讓視圖更新則需要調(diào)用專(zhuān)用的方法 this.setState
- state 它是類(lèi)的一個(gè)成員屬性
- state 中的數(shù)據(jù)可讀可寫(xiě)的
使用:
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í),讓視圖更新,用到專(zhuān)門(mén)用來(lái)修改 state 中數(shù)據(jù)的方法 setState。
語(yǔ)法:
# 語(yǔ)法1
this.setState({
key:value
})
# 語(yǔ)法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ù)并沒(méi)有發(fā)生改變,說(shuō)明這里的更新數(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ǔ)充說(shuō)明:批處理隊(duì)列的底層進(jìn)行的是[...obj1,...obj2]的操作,所以重復(fù)的項(xiàng)只會(huì)出現(xiàn)一次。而函數(shù)方式的批處理進(jìn)行的是[fn1,fn2...],所以會(huì)依次執(zhí)行。這里的解釋過(guò)于牽強(qiáng)和抽象,只是為了簡(jiǎn)單理解和記憶,更加完整的闡述需要了解底層之后,再加深理解。
關(guān)于 setState 方法的同步操作:
react17 及之前版本,如果你把當(dāng)前的操作不寫(xiě)在合成事件中,則 setState 它就是同步的,react18全是異步的。
setState 在執(zhí)行的過(guò)程中,它會(huì)判斷當(dāng)?shù)膱?zhí)行環(huán)境,如果為宏任務(wù)等,則它會(huì)立即執(zhí)行。
也就是說(shuō), setState 方法只要寫(xiě)在合成事件處理方法中,它就是異步的;只要不寫(xiě)在合成事件中,它都是同步。(僅限于 react17 及之前版本)
比如 setState 方法寫(xiě)在宏任務(wù)中或者寫(xiě)在原生事件中就是同步的。
例如下面這樣的寫(xiě)法就是同步操作:
import React, { Component } from 'react';
class App extends Component {
// setState它是同步的也是異步的
// 只要寫(xiě)在合成事件處理方法中,它就是異步
// 只要不寫(xiě)在合成事件中,它都是同步
state = {
num: 100
}
addNum(evt, n = 1) {
setTimeout(() => {
// 寫(xiě)在宏任務(wù)中的setState它是同步的
this.setState({
num: this.state.num + 3
})
console.log(this.state.num);
}, 0);
}
// 類(lèi)似于vue中的mounted方法
// componentDidMount() {
// // 寫(xiě)在原生事件中它的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() // 這里也可以寫(xiě)成這一句:this.setState({})
// 如果你setState寫(xiě)了一個(gè)空對(duì)象,則它會(huì)更新視圖一次
// this.setState({})
// 如果你寫(xiě)了為null,setState不會(huì)做任何事件,相當(dāng)于沒(méi)有調(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寫(xiě)了一個(gè)空對(duì)象,則它會(huì)更新視圖一次
this.setState({})
如果你寫(xiě)了為null,setState不會(huì)做任何事件,相當(dāng)于沒(méi)有調(diào)用
this.setState(null)
4. 案例-購(gòu)物車(chē)
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>名稱(chēng)</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組件創(chuàng)建與事件綁定的實(shí)現(xiàn)方法
react事件綁定時(shí)。this并不會(huì)指向當(dāng)前DOM元素。往往使用bind來(lái)改變this指向,今天通過(guò)本文給大家介紹React事件綁定的方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-12-12
React使用useEffect解決setState副作用詳解
這篇文章主要為大家介紹了React使用useEffect解決setState副作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
react實(shí)現(xiàn)移動(dòng)端下拉菜單的示例代碼
這篇文章主要介紹了react實(shí)現(xiàn)移動(dòng)端下拉菜單的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
nodejs和react實(shí)現(xiàn)即時(shí)通訊簡(jiǎn)易聊天室功能
這篇文章主要介紹了nodejs和react實(shí)現(xiàn)即時(shí)通訊簡(jiǎn)易聊天室功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08
淺談對(duì)于react-thunk中間件的簡(jiǎn)單理解
這篇文章主要介紹了淺談對(duì)于react-thunk中間件的簡(jiǎn)單理解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
使用react-native-image-viewer實(shí)現(xiàn)大圖預(yù)覽
這篇文章主要介紹了使用react-native-image-viewer實(shí)現(xiàn)大圖預(yù)覽,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法
這篇文章主要介紹了React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-12-12

