React state狀態(tài)屬性詳細(xì)講解
1. 基本使用
要點:
- 成員屬性 state 它是一個特殊的屬性,它是當(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的值是對象,表示一個組件中可以有多個數(shù)據(jù)
- 通過this.state來獲取狀態(tài),react 中沒有做數(shù)據(jù)代理
- 想要修改 state 數(shù)據(jù)值同時讓視圖更新則需要調(diào)用專用的方法 this.setState
- state 它是類的一個成員屬性
- 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ù),它不會觸發(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ù)的同時,讓視圖更新,用到專門用來修改 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) { // 對象方式更新 批處理 => 數(shù)組【隊列】 虛擬dom比對 // 它是一個異步操作 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ā)生了更新,但是控制臺打印 state 中的數(shù)據(jù)并沒有發(fā)生改變,說明這里的更新數(shù)據(jù)是異步操作。
之所以是異步操作,是因為 React 在這里做了批處理。即當(dāng)執(zhí)行多次修改數(shù)據(jù)的操作時,React 并不會立即執(zhí)行,而是將這些操作存入一個異步隊列中,然后再進(jìn)行統(tǒng)一處理,這樣做可以提升性能。假如我現(xiàn)在要修改 3 次數(shù)據(jù),不用批處理需要更新視圖 3 次,而如果進(jìn)行批處理的話,只需要更新視圖一次。更新視圖需要進(jìn)行 dom 比對,只比 1 次顯然比比對 3 次性能更好。
關(guān)于 setState 方法(React 批處理)中的對象合并:
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ù),它不會合并,它會依次執(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;
從圖中運行結(jié)果可以看出,當(dāng)我們多次修改數(shù)據(jù)時,視圖只針對最后一次修改作出渲染。這是因為在 React 批處理中(批處理隊列是一個集合,這里看作是一個對象),會進(jìn)行對象合并。什么意思呢?首先對象的 key 值是唯一的,當(dāng) key 值 num 已經(jīng)存在時,再傳 num 會對對象中已經(jīng)存在的 num 進(jìn)行修改,即對象中有效的 num 的值是最后一次操作:this.state.num + 3
。
補(bǔ)充說明:批處理隊列的底層進(jìn)行的是[...obj1,...obj2]
的操作,所以重復(fù)的項只會出現(xiàn)一次。而函數(shù)方式的批處理進(jìn)行的是[fn1,fn2...]
,所以會依次執(zhí)行。這里的解釋過于牽強(qiáng)和抽象,只是為了簡單理解和記憶,更加完整的闡述需要了解底層之后,再加深理解。
關(guān)于 setState 方法的同步操作:
react17 及之前版本,如果你把當(dāng)前的操作不寫在合成事件中,則 setState 它就是同步的,react18全是異步的。
setState 在執(zhí)行的過程中,它會判斷當(dāng)?shù)膱?zhí)行環(huán)境,如果為宏任務(wù)等,則它會立即執(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寫了一個空對象,則它會更新視圖一次 // this.setState({}) // 如果你寫了為null,setState不會做任何事件,相當(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寫了一個空對象,則它會更新視圖一次
this.setState({})
如果你寫了為null,setState不會做任何事件,相當(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>價格</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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React組件創(chuàng)建與事件綁定的實現(xiàn)方法
react事件綁定時。this并不會指向當(dāng)前DOM元素。往往使用bind來改變this指向,今天通過本文給大家介紹React事件綁定的方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-12-12React使用useEffect解決setState副作用詳解
這篇文章主要為大家介紹了React使用useEffect解決setState副作用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10nodejs和react實現(xiàn)即時通訊簡易聊天室功能
這篇文章主要介紹了nodejs和react實現(xiàn)即時通訊簡易聊天室功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-08-08使用react-native-image-viewer實現(xiàn)大圖預(yù)覽
這篇文章主要介紹了使用react-native-image-viewer實現(xiàn)大圖預(yù)覽,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法
這篇文章主要介紹了React數(shù)據(jù)傳遞之組件內(nèi)部通信的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12