React中state屬性案例詳解
通過(guò)之前的文章自定義組件,我們發(fā)現(xiàn)創(chuàng)建的類式組件里面會(huì)存在state
屬性。在React中,state
是一個(gè)用于存儲(chǔ)組件內(nèi)部數(shù)據(jù)的特殊對(duì)象。每個(gè)React組件都可以包含自己的state
,我們往往是通過(guò)修改state
的值來(lái)驅(qū)動(dòng)React重新渲染組件。
由于函數(shù)式組件this
指向的是undifind
所以,函數(shù)式組件不存在state
。
1)state案例
需求:點(diǎn)擊按鈕,切換標(biāo)題內(nèi)容
<script type="text/babel"> // 創(chuàng)建類式組件 class MyComponent extends React.Component { render() { console.log(this) return ( <div> <h1>內(nèi)容</h1> <button>按鈕</button> </div> ); } } /*渲染組件到頁(yè)面*/ ReactDOM.render(<MyComponent/>, document.getElementById("demo")) </script>
我們是通過(guò)修改state
的值來(lái)驅(qū)動(dòng)內(nèi)容的變化,那么我們想要使內(nèi)容變化,按鈕的點(diǎn)擊事件就是修改state
的值。那怎么初始化state呢,我們知道,state
是實(shí)例的屬性,所以我們想要初始化state
的值需要在構(gòu)造器里面實(shí)現(xiàn)。
構(gòu)造器里面需要接受參數(shù),應(yīng)該是什么參數(shù)呢,通過(guò)查詢官網(wǎng),里面?zhèn)鬟f的參數(shù)為props
,即:
class MyComponent extends React.Component { constructor(props) { super(props); } render() { return ( <div> <h1>內(nèi)容</h1> <button>按鈕</button> </div> ); } }
2)初始化state
有了構(gòu)造器我們就可以在構(gòu)造器里面初始化state的值了
class MyComponent extends React.Component { constructor(props, context, updater) { super(props, context, updater); // 初始化state this.state = { count: 0 } } render() { return ( <div> <h1>{this.state.count}</h1> <button>按鈕</button> </div> ); } }
此時(shí)就可以查看到state的值了:
3)原生JS事件綁定
原生的 JavaScript 綁定事件有多種方式,以下是其中一些常見(jiàn)的方法:
使用 addEventListener
方法: 這是一種推薦的方式,可以用于為一個(gè) DOM 元素添加事件監(jiān)聽(tīng)器。它可以用于綁定多個(gè)事件處理函數(shù),而不會(huì)覆蓋之前綁定的處理函數(shù)。
var element = document.getElementById("myElement"); element.addEventListener("click", myFunction);
內(nèi)聯(lián)事件處理函數(shù): 你可以將事件處理函數(shù)直接寫在 HTML 元素的屬性中,如 onclick
、onmouseover
等。這不太推薦,因?yàn)閷?HTML 和 JavaScript 混合在一起可能會(huì)使代碼不夠清晰。
<button onclick="myFunction()">點(diǎn)擊我</button>
DOM 屬性方式: 你可以直接為 DOM 元素的事件處理屬性分配函數(shù),這與內(nèi)聯(lián)事件處理函數(shù)類似,但是更清晰一些。
var element = document.getElementById("myElement"); element.onclick = myFunction;
4)React事件綁定
上面提到了原生的JS事件綁定方式,除了內(nèi)斂事件處理函數(shù)其他都需要操作DOM,而我們的React是不需要操作DOM的,因此我們使用內(nèi)斂事件處理函數(shù)對(duì)元素添加事件綁定。
class MyComponent extends React.Component { constructor(props, context, updater) { super(props, context, updater); this.state = { count: 0 } } render() { return ( <div> <h1>{this.state.count}</h1> <button οnclick="increment()">按鈕</button> </div> ); } } //注冊(cè)點(diǎn)擊事件 function increment() { console.log("按鈕被點(diǎn)擊了") }
上面通過(guò)
onClick="increment()"
給按鈕注冊(cè)了點(diǎn)擊事件
通過(guò)上面的注冊(cè)事件我們發(fā)現(xiàn)控制臺(tái)報(bào)錯(cuò)了
根據(jù)錯(cuò)誤提示,告訴我們?cè)趪L試使用 onclick
事件處理屬性時(shí)出現(xiàn)錯(cuò)誤,提示是否想使用 onClick
。所以我們把onclick
變成onClick
。
render() { return ( <div> <h1>{this.state.count}</h1> <button onClick="increment()">按鈕</button> </div> ); }
但是發(fā)現(xiàn)還是報(bào)錯(cuò)了
報(bào)錯(cuò)的意思是期望 onClick
事件監(jiān)聽(tīng)器是一個(gè)函數(shù),但實(shí)際上它是一個(gè)字符串類型的值。我們嘗試使用JSX的表達(dá)式將函數(shù)賦值給onClick
事件
<button onClick={increment()}>按鈕</button>
此時(shí)我們發(fā)現(xiàn),我們還沒(méi)有點(diǎn)擊按鈕,一刷新網(wǎng)頁(yè),控制臺(tái)立馬打印了按鈕被點(diǎn)擊了
這句話,也就是說(shuō)在刷新網(wǎng)頁(yè)的時(shí)候increment()
立即執(zhí)行了,點(diǎn)擊按鈕卻得不到我們想要的結(jié)果。
我們?cè)賮?lái)分析一下onClick={increment()}
,這里面其實(shí)是將increment()
執(zhí)行結(jié)束之后的返回值賦值給onClick
,而不是將函數(shù)賦值給onClick
,所以increment()
立即執(zhí)行,然后將返回的undefined
賦值給onClick
,所以點(diǎn)擊按鈕當(dāng)然沒(méi)有作用,我們能做的就是把increment
函數(shù)本身賦值給onClick
:
<button onClick={increment}>按鈕</button>
此時(shí)點(diǎn)擊按鈕成功打印結(jié)果,控制臺(tái)也不存在報(bào)錯(cuò)。
5)類中方法this指向
解決了點(diǎn)擊事件,我們下面的目標(biāo)就修改state里面的count,我們能直接在increment
函數(shù)里面修改嗎?組件的實(shí)例并不是我們創(chuàng)建的,而且函數(shù)在組件(類)的外面,increment
的this
指向的是undefined
,也就是說(shuō)increment
函數(shù)根本訪問(wèn)不到組件的this
,從而也就訪問(wèn)不到組件的state
。
我們可以把increment
函數(shù)寫在類組件里面,作為類的一般方法。
class MyComponent extends React.Component { constructor(props, context, updater) { super(props, context, updater); this.state = { count: 0 } } render() { return ( <div> <h1>{this.state.count}</h1> <button onClick={increment}>按鈕</button> </div> ); } // 注意作為一般方法,不需要使用function修飾 increment() { console.log("按鈕被點(diǎn)擊了"+this.state.count) } }
此時(shí)我們發(fā)現(xiàn)控制臺(tái)又報(bào)錯(cuò)了,提示我們increment
未定義,可是我們已經(jīng)定義了的
我們實(shí)現(xiàn)的increment
函數(shù)作為一般方法,可以通過(guò)類的實(shí)例調(diào)用,而onClick={increment}
根本訪問(wèn)不到,所以修改成下面:
<button onClick={this.increment}>按鈕</button>
此時(shí)報(bào)錯(cuò)解決了,但是我們點(diǎn)擊按鈕的時(shí)候,報(bào)錯(cuò)又接踵而至:
根據(jù)提示我們發(fā)現(xiàn)state
讀取不到,可是我們的state
就是掛載在this
上面的屬性,怎么會(huì)讀取不到呢,我們通過(guò)在increment
函數(shù)打印this
查看:
increment() { console.log(this) console.log("按鈕被點(diǎn)擊了"+this.state.count) }
我們發(fā)現(xiàn)打印的結(jié)果是undefined
,這令我們很奇怪,方法定義在類里面,怎么會(huì)訪問(wèn)不到this
呢,而同樣為類的方法render
卻可以訪問(wèn)到this
呢?
我們?cè)倩仡櫼幌聞?chuàng)建類式組件以及渲染類式組件的過(guò)程,我們定義組件重寫了render
方法,返回VDOM,而React想要渲染DOM需要?jiǎng)?chuàng)建組件的實(shí)例,然后**通過(guò)實(shí)例調(diào)用render
**方法。我們知道類里面的方法是掛載到原型對(duì)象上的,只有通過(guò)類的實(shí)例調(diào)用方法才能訪問(wèn)到this
,而我們創(chuàng)建的組件實(shí)例并不是有我們創(chuàng)建的,我們只是把increment
函數(shù)賦值給onClick
,所以increment
可能不是通過(guò)實(shí)例去調(diào)用的。
6)類中方法this指向案例
我們創(chuàng)建一個(gè)類,類里面有speak方法,用于打印this
class Person { constructor(name, age) { this.name = name this.age = age } speak(){ console.log(this) } }
創(chuàng)建實(shí)例,調(diào)用方法
const p=new Person('jvyou',22) p.speak()
此時(shí)成功打印出來(lái)p的this
,說(shuō)明通過(guò)類的實(shí)例調(diào)用類里面的方法,方法里面的this
就是指向?qū)嵗?/p>
我們將p.speak
賦值給其他變量嘗試直接調(diào)用試試:
const s = p.speak s()
輸出的結(jié)果卻是undefined
,這個(gè)屬于直接調(diào)用,原本的this
應(yīng)該是window
,但是由于嚴(yán)格模式下就是undefind
通過(guò)這個(gè)案例我們?cè)倩仡櫼韵挛覀兊拇a:
<button onClick={this.increment}>按鈕</button>
我們將this.increment
賦值給onClick
,在點(diǎn)擊按鈕的時(shí)候React會(huì)直接調(diào)用onClick
,根本不是通過(guò)組件的實(shí)例調(diào)用的,所以根本訪問(wèn)不到組件的this
,所以也訪問(wèn)不到組件的state
。
7)解決this指向undefined
我們知道可以通過(guò)bind
方法給函數(shù)綁定this
,我們可以在構(gòu)造函數(shù)里面訪問(wèn)到this
,我們可以通過(guò)在構(gòu)造函數(shù)里面給increment
方法綁定實(shí)例的this
,再將綁定成功的新的函數(shù)掛載到對(duì)象的屬性上。
constructor(props, context, updater) { super(props, context, updater); this.state = { count: 0 } this.increment = this.increment.bind(this) }
this.increment.bind(this)
綁定this
之后返回一個(gè)新的函數(shù),此時(shí)函數(shù)內(nèi)部的this
指向的就是創(chuàng)建的對(duì)象再將返回的函數(shù)賦值給
this.increment
,作為屬性掛載到對(duì)象上。
此時(shí)再點(diǎn)擊按鈕,成功打印出組件的this
,且發(fā)現(xiàn)increment
也變成組件屬性了,且原型鏈上面也存在increment
方法,屬性上的increment
是通過(guò)原型鏈上的increment
綁定this
得來(lái)的,因此onClick={this.increment}
是將屬性上的increment
賦值給onClick
。
8)修改state
通過(guò)上面的方式我們就可以在方法里面修改this.state
:
increment() { this.state.count++ console.log("按鈕被點(diǎn)擊了" + this.state.count) }
但是我們發(fā)現(xiàn)點(diǎn)擊了按鈕不報(bào)錯(cuò),count的值也一直為0不改變
這是因?yàn)?code>state不能直接更改,需要使用React
的內(nèi)置API
。
我們可以通過(guò)打印組件的this,在原型鏈上找到API:setState
,所以可以直接通過(guò)this
訪問(wèn)即可
increment() { const c = this.state.count + 1 this.setState({count: c}) console.log("按鈕被點(diǎn)擊了" + this.state.count) }
此時(shí)成功解決。但是在實(shí)現(xiàn)的時(shí)候使用了const c = this.state.count + 1
,嫩不能改成const c = this.state.count++
呢,是不能的,因?yàn)檫@二樣會(huì)直接修改state
的值。
state存在多個(gè)屬性怎么修改呢
constructor(props, context, updater) { super(props, context, updater); this.state = { count: 0, name: '橘柚' } this.increment = this.increment.bind(this) }
上面代碼state里面存在兩個(gè)屬性
increment() { const c = this.state.count + 1 this.setState({count: c}) }
這里面只修改了count
,但不會(huì)影響到name
9)state的簡(jiǎn)寫
簡(jiǎn)化state
我們?cè)谏厦娉跏蓟?code>state的時(shí)候是在構(gòu)造器里面實(shí)現(xiàn)的,但是我們知道,類里面可以直接定義類的屬性,而且可以直接給屬性賦值,所以我們就不需要在構(gòu)造器里面初始化state
,直接修改成:
class MyComponent extends React.Component { //初始化state,直接作為類組件的屬性定義 state = { count: 0, name: '橘柚' } // 構(gòu)造器 constructor(props, context, updater) { super(props, context, updater); this.increment = this.increment.bind(this) } ...省略render和increment }
簡(jiǎn)化事件
我們知道increment
函數(shù)在MyComponent
原型鏈上不通過(guò)實(shí)例調(diào)用會(huì)導(dǎo)致this
指向丟失,所以我們通過(guò)bind
掛載this
解決,即this.increment = this.increment.bind(this)
,但是我們知道箭頭函數(shù)的 this
指向是與普通函數(shù)不同的。箭頭函數(shù)的 this
指向是繼承自包含它的最近的父級(jí)(詞法作用域)函數(shù)的 this
值,而不是動(dòng)態(tài)綁定的。所以只需要將increment
變成箭頭函數(shù),在創(chuàng)建實(shí)例的時(shí)候increment
的this
自己就會(huì)指向?qū)嵗?,無(wú)需再次綁定。
increment = () => { const c = this.state.count + 1 this.setState({count: c}) console.log("按鈕被點(diǎn)擊了" + this.state.count) }
此時(shí)我們打印this
,也能在組件的屬性上面找到increment
。
一開(kāi)始我們添加構(gòu)造器是為了初始化state以及為事件綁定this,現(xiàn)在全部解決了,構(gòu)造器也就不用存在了,所以最后的代碼如下:
class MyComponent extends React.Component { state = { count: 0, name: '橘柚' } increment = () => { const c = this.state.count + 1 this.setState({count: c}) console.log("按鈕被點(diǎn)擊了" + this.state.count) } render() { return ( <div> <h1>{this.state.count}{this.state.name}</h1> <button onClick={this.increment}>按鈕</button> </div> ); } }
10)setState 擴(kuò)展
對(duì)象式setState
我們可以使用 setState
來(lái)更新組件的狀態(tài),但是 setState
有兩種常見(jiàn)的寫法,上面我們一直用的是傳遞一個(gè)對(duì)象,即:
this.setState({ count: this.state.count + 1 });
這種寫法直接傳遞一個(gè)新的狀態(tài)對(duì)象給 setState
。React會(huì)將新?tīng)顟B(tài)與當(dāng)前狀態(tài)合并,并在后續(xù)的更新周期中應(yīng)用這個(gè)新?tīng)顟B(tài)。
但其實(shí)setState
還有第二個(gè)參數(shù):
setState( stateChange , [callback] )
stateChange :狀態(tài)改變對(duì)象? callback:可選回調(diào)函數(shù),它在狀態(tài)更新完畢,render調(diào)用后,才被調(diào)用
這個(gè)回調(diào)有什么用呢,我們先看一個(gè)案例:
export default class Count extends Component { state = { count: 0 } increment = () => { const {count} = this.state this.setState({count: count + 1}) console.log("@count:", this.state.count) } render() { return ( <div> <h1>當(dāng)前Count:{this.state.count}</h1> <button onClick={this.increment}>+1</button> </div> ) } }
當(dāng)我們點(diǎn)擊了“+1”的按鈕之后,頁(yè)面展示的效果確實(shí)是1,但是打印的結(jié)果卻是0,我們?cè)诖蛴ount的時(shí)候已經(jīng)執(zhí)行 this.setState({count: count + 1})
進(jìn)行狀態(tài)更新了,為什么還是0呢?那是因?yàn)?code>setState 可能是異步的,所以在讀取 this.state
之前不應(yīng)該依賴于它的值。
如果需要在狀態(tài)更新后執(zhí)行一些操作,可以將這些操作放在 setState
的回調(diào)函數(shù)中:
increment = () => { const {count} = this.state this.setState({count: count + 1}, () => { console.log("@count:", this.state.count) }) }
函數(shù)式setState
傳遞一個(gè)更新函數(shù):
this.setState((prevState, props ) => { return { count: prevState.count + 1 }; } , [callback]);
這種寫法將狀態(tài)的更新邏輯封裝在一個(gè)函數(shù)中,并接受參數(shù) prevState
和props
,prevState
代表當(dāng)前狀態(tài)的先前值。
這兩種寫法的選擇取決于你的需求和使用情境。通常情況下,使用更新函數(shù)的方式更加安全,因?yàn)樗梢源_?;诋?dāng)前狀態(tài)來(lái)計(jì)算新?tīng)顟B(tài),避免了潛在的競(jìng)態(tài)條件和不一致性。另外,使用更新函數(shù)的方式也適用于異步更新?tīng)顟B(tài)的情況。
到此這篇關(guān)于React中state屬性的文章就介紹到這了,更多相關(guān)React state屬性內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Axios在React中請(qǐng)求數(shù)據(jù)的方法詳解
這篇文章主要給大家介紹了初學(xué)React,如何規(guī)范的在react中請(qǐng)求數(shù)據(jù),主要介紹了使用axios進(jìn)行簡(jiǎn)單的數(shù)據(jù)獲取,加入狀態(tài)變量,優(yōu)化交互體驗(yàn),自定義hook進(jìn)行數(shù)據(jù)獲取和使用useReducer改造請(qǐng)求,本文主要適合于剛接觸React的初學(xué)者以及不知道如何規(guī)范的在React中獲取數(shù)據(jù)的人2023-09-09React?Context源碼實(shí)現(xiàn)原理詳解
這篇文章主要為大家介紹了React?Context源碼實(shí)現(xiàn)原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10基于react框架使用的一些細(xì)節(jié)要點(diǎn)的思考
下面小編就為大家?guī)?lái)一篇基于react框架使用的一些細(xì)節(jié)要點(diǎn)的思考。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05如何應(yīng)用?SOLID?原則在?React?中整理代碼之開(kāi)閉原則
React?不是面向?qū)ο?,但這些原則背后的主要思想可能是有幫助的,在本文中,我將嘗試演示如何應(yīng)用這些原則來(lái)編寫更好的代碼,對(duì)React?SOLID原則開(kāi)閉原則相關(guān)知識(shí)感興趣的朋友一起看看吧2022-07-07深入理解react-router 路由的實(shí)現(xiàn)原理
這篇文章主要介紹了深入理解react-router 路由的實(shí)現(xiàn)原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09react使用useState修改對(duì)象或者數(shù)組的值無(wú)法改變視圖的問(wèn)題
這篇文章主要介紹了react使用useState修改對(duì)象或者數(shù)組的值無(wú)法改變視圖的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08