React中state屬性案例詳解
通過之前的文章自定義組件,我們發(fā)現(xiàn)創(chuàng)建的類式組件里面會存在state屬性。在React中,state 是一個用于存儲組件內部數據的特殊對象。每個React組件都可以包含自己的state,我們往往是通過修改state的值來驅動React重新渲染組件。
由于函數式組件this指向的是undifind所以,函數式組件不存在state。
1)state案例
需求:點擊按鈕,切換標題內容
<script type="text/babel">
// 創(chuàng)建類式組件
class MyComponent extends React.Component {
render() {
console.log(this)
return (
<div>
<h1>內容</h1>
<button>按鈕</button>
</div>
);
}
}
/*渲染組件到頁面*/
ReactDOM.render(<MyComponent/>, document.getElementById("demo"))
</script>我們是通過修改state的值來驅動內容的變化,那么我們想要使內容變化,按鈕的點擊事件就是修改state的值。那怎么初始化state呢,我們知道,state是實例的屬性,所以我們想要初始化state的值需要在構造器里面實現(xiàn)。
構造器里面需要接受參數,應該是什么參數呢,通過查詢官網,里面?zhèn)鬟f的參數為props,即:
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>內容</h1>
<button>按鈕</button>
</div>
);
}
}2)初始化state
有了構造器我們就可以在構造器里面初始化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>
);
}
}此時就可以查看到state的值了:

3)原生JS事件綁定
原生的 JavaScript 綁定事件有多種方式,以下是其中一些常見的方法:
使用 addEventListener 方法: 這是一種推薦的方式,可以用于為一個 DOM 元素添加事件監(jiān)聽器。它可以用于綁定多個事件處理函數,而不會覆蓋之前綁定的處理函數。
var element = document.getElementById("myElement");
element.addEventListener("click", myFunction);內聯(lián)事件處理函數: 你可以將事件處理函數直接寫在 HTML 元素的屬性中,如 onclick、onmouseover 等。這不太推薦,因為將 HTML 和 JavaScript 混合在一起可能會使代碼不夠清晰。
<button onclick="myFunction()">點擊我</button>
DOM 屬性方式: 你可以直接為 DOM 元素的事件處理屬性分配函數,這與內聯(lián)事件處理函數類似,但是更清晰一些。
var element = document.getElementById("myElement");
element.onclick = myFunction;4)React事件綁定
上面提到了原生的JS事件綁定方式,除了內斂事件處理函數其他都需要操作DOM,而我們的React是不需要操作DOM的,因此我們使用內斂事件處理函數對元素添加事件綁定。
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>
);
}
}
//注冊點擊事件
function increment() {
console.log("按鈕被點擊了")
}上面通過
onClick="increment()"給按鈕注冊了點擊事件
通過上面的注冊事件我們發(fā)現(xiàn)控制臺報錯了

根據錯誤提示,告訴我們在嘗試使用 onclick 事件處理屬性時出現(xiàn)錯誤,提示是否想使用 onClick。所以我們把onclick變成onClick。
render() {
return (
<div>
<h1>{this.state.count}</h1>
<button onClick="increment()">按鈕</button>
</div>
);
}但是發(fā)現(xiàn)還是報錯了

報錯的意思是期望 onClick 事件監(jiān)聽器是一個函數,但實際上它是一個字符串類型的值。我們嘗試使用JSX的表達式將函數賦值給onClick事件
<button onClick={increment()}>按鈕</button>此時我們發(fā)現(xiàn),我們還沒有點擊按鈕,一刷新網頁,控制臺立馬打印了按鈕被點擊了這句話,也就是說在刷新網頁的時候increment()立即執(zhí)行了,點擊按鈕卻得不到我們想要的結果。
我們再來分析一下onClick={increment()},這里面其實是將increment()執(zhí)行結束之后的返回值賦值給onClick,而不是將函數賦值給onClick,所以increment()立即執(zhí)行,然后將返回的undefined賦值給onClick,所以點擊按鈕當然沒有作用,我們能做的就是把increment函數本身賦值給onClick:
<button onClick={increment}>按鈕</button>此時點擊按鈕成功打印結果,控制臺也不存在報錯。
5)類中方法this指向
解決了點擊事件,我們下面的目標就修改state里面的count,我們能直接在increment函數里面修改嗎?組件的實例并不是我們創(chuàng)建的,而且函數在組件(類)的外面,increment的this指向的是undefined,也就是說increment函數根本訪問不到組件的this,從而也就訪問不到組件的state。
我們可以把increment函數寫在類組件里面,作為類的一般方法。
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("按鈕被點擊了"+this.state.count)
}
}此時我們發(fā)現(xiàn)控制臺又報錯了,提示我們increment未定義,可是我們已經定義了的

我們實現(xiàn)的increment函數作為一般方法,可以通過類的實例調用,而onClick={increment}根本訪問不到,所以修改成下面:
<button onClick={this.increment}>按鈕</button>此時報錯解決了,但是我們點擊按鈕的時候,報錯又接踵而至:

根據提示我們發(fā)現(xiàn)state讀取不到,可是我們的state就是掛載在this上面的屬性,怎么會讀取不到呢,我們通過在increment函數打印this查看:
increment() {
console.log(this)
console.log("按鈕被點擊了"+this.state.count)
}我們發(fā)現(xiàn)打印的結果是undefined,這令我們很奇怪,方法定義在類里面,怎么會訪問不到this呢,而同樣為類的方法render卻可以訪問到this呢?
我們再回顧一下創(chuàng)建類式組件以及渲染類式組件的過程,我們定義組件重寫了render方法,返回VDOM,而React想要渲染DOM需要創(chuàng)建組件的實例,然后**通過實例調用render**方法。我們知道類里面的方法是掛載到原型對象上的,只有通過類的實例調用方法才能訪問到this,而我們創(chuàng)建的組件實例并不是有我們創(chuàng)建的,我們只是把increment函數賦值給onClick,所以increment可能不是通過實例去調用的。
6)類中方法this指向案例
我們創(chuàng)建一個類,類里面有speak方法,用于打印this
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
speak(){
console.log(this)
}
}創(chuàng)建實例,調用方法
const p=new Person('jvyou',22)
p.speak()此時成功打印出來p的this,說明通過類的實例調用類里面的方法,方法里面的this就是指向實例的

我們將p.speak賦值給其他變量嘗試直接調用試試:
const s = p.speak s()
輸出的結果卻是undefined,這個屬于直接調用,原本的this應該是window,但是由于嚴格模式下就是undefind
通過這個案例我們再回顧以下我們的代碼:
<button onClick={this.increment}>按鈕</button>我們將this.increment賦值給onClick,在點擊按鈕的時候React會直接調用onClick,根本不是通過組件的實例調用的,所以根本訪問不到組件的this,所以也訪問不到組件的state。
7)解決this指向undefined
我們知道可以通過bind方法給函數綁定this,我們可以在構造函數里面訪問到this,我們可以通過在構造函數里面給increment方法綁定實例的this,再將綁定成功的新的函數掛載到對象的屬性上。
constructor(props, context, updater) {
super(props, context, updater);
this.state = {
count: 0
}
this.increment = this.increment.bind(this)
}
this.increment.bind(this)綁定this之后返回一個新的函數,此時函數內部的this指向的就是創(chuàng)建的對象再將返回的函數賦值給
this.increment,作為屬性掛載到對象上。
此時再點擊按鈕,成功打印出組件的this,且發(fā)現(xiàn)increment也變成組件屬性了,且原型鏈上面也存在increment方法,屬性上的increment是通過原型鏈上的increment綁定this得來的,因此onClick={this.increment}是將屬性上的increment賦值給onClick。
8)修改state
通過上面的方式我們就可以在方法里面修改this.state:
increment() {
this.state.count++
console.log("按鈕被點擊了" + this.state.count)
}但是我們發(fā)現(xiàn)點擊了按鈕不報錯,count的值也一直為0不改變
這是因為state不能直接更改,需要使用React的內置API。
我們可以通過打印組件的this,在原型鏈上找到API:setState,所以可以直接通過this訪問即可

increment() {
const c = this.state.count + 1
this.setState({count: c})
console.log("按鈕被點擊了" + this.state.count)
}此時成功解決。但是在實現(xiàn)的時候使用了const c = this.state.count + 1,嫩不能改成const c = this.state.count++呢,是不能的,因為這二樣會直接修改state的值。
state存在多個屬性怎么修改呢
constructor(props, context, updater) {
super(props, context, updater);
this.state = {
count: 0,
name: '橘柚'
}
this.increment = this.increment.bind(this)
}上面代碼state里面存在兩個屬性
increment() {
const c = this.state.count + 1
this.setState({count: c})
}這里面只修改了count,但不會影響到name
9)state的簡寫
簡化state
我們在上面初始化state的時候是在構造器里面實現(xiàn)的,但是我們知道,類里面可以直接定義類的屬性,而且可以直接給屬性賦值,所以我們就不需要在構造器里面初始化state,直接修改成:
class MyComponent extends React.Component {
//初始化state,直接作為類組件的屬性定義
state = {
count: 0,
name: '橘柚'
}
// 構造器
constructor(props, context, updater) {
super(props, context, updater);
this.increment = this.increment.bind(this)
}
...省略render和increment
}簡化事件
我們知道increment函數在MyComponent原型鏈上不通過實例調用會導致this指向丟失,所以我們通過bind掛載this解決,即this.increment = this.increment.bind(this),但是我們知道箭頭函數的 this 指向是與普通函數不同的。箭頭函數的 this 指向是繼承自包含它的最近的父級(詞法作用域)函數的 this 值,而不是動態(tài)綁定的。所以只需要將increment變成箭頭函數,在創(chuàng)建實例的時候increment的this自己就會指向實例,無需再次綁定。
increment = () => {
const c = this.state.count + 1
this.setState({count: c})
console.log("按鈕被點擊了" + this.state.count)
}此時我們打印this,也能在組件的屬性上面找到increment。

一開始我們添加構造器是為了初始化state以及為事件綁定this,現(xiàn)在全部解決了,構造器也就不用存在了,所以最后的代碼如下:
class MyComponent extends React.Component {
state = {
count: 0,
name: '橘柚'
}
increment = () => {
const c = this.state.count + 1
this.setState({count: c})
console.log("按鈕被點擊了" + this.state.count)
}
render() {
return (
<div>
<h1>{this.state.count}{this.state.name}</h1>
<button onClick={this.increment}>按鈕</button>
</div>
);
}
}10)setState 擴展
對象式setState
我們可以使用 setState 來更新組件的狀態(tài),但是 setState 有兩種常見的寫法,上面我們一直用的是傳遞一個對象,即:
this.setState({ count: this.state.count + 1 });這種寫法直接傳遞一個新的狀態(tài)對象給 setState。React會將新狀態(tài)與當前狀態(tài)合并,并在后續(xù)的更新周期中應用這個新狀態(tài)。
但其實setState還有第二個參數:
setState( stateChange , [callback] )
stateChange :狀態(tài)改變對象? callback:可選回調函數,它在狀態(tài)更新完畢,render調用后,才被調用
這個回調有什么用呢,我們先看一個案例:
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>當前Count:{this.state.count}</h1>
<button onClick={this.increment}>+1</button>
</div>
)
}
}當我們點擊了“+1”的按鈕之后,頁面展示的效果確實是1,但是打印的結果卻是0,我們在打印count的時候已經執(zhí)行 this.setState({count: count + 1})進行狀態(tài)更新了,為什么還是0呢?那是因為setState 可能是異步的,所以在讀取 this.state 之前不應該依賴于它的值。

如果需要在狀態(tài)更新后執(zhí)行一些操作,可以將這些操作放在 setState 的回調函數中:
increment = () => {
const {count} = this.state
this.setState({count: count + 1}, () => {
console.log("@count:", this.state.count)
})
}函數式setState
傳遞一個更新函數:
this.setState((prevState, props ) => {
return { count: prevState.count + 1 };
} , [callback]);這種寫法將狀態(tài)的更新邏輯封裝在一個函數中,并接受參數 prevState和props,prevState代表當前狀態(tài)的先前值。
這兩種寫法的選擇取決于你的需求和使用情境。通常情況下,使用更新函數的方式更加安全,因為它可以確保基于當前狀態(tài)來計算新狀態(tài),避免了潛在的競態(tài)條件和不一致性。另外,使用更新函數的方式也適用于異步更新狀態(tài)的情況。
到此這篇關于React中state屬性的文章就介紹到這了,更多相關React state屬性內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
如何應用?SOLID?原則在?React?中整理代碼之開閉原則
React?不是面向對象,但這些原則背后的主要思想可能是有幫助的,在本文中,我將嘗試演示如何應用這些原則來編寫更好的代碼,對React?SOLID原則開閉原則相關知識感興趣的朋友一起看看吧2022-07-07
深入理解react-router 路由的實現(xiàn)原理
這篇文章主要介紹了深入理解react-router 路由的實現(xiàn)原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-09-09
react使用useState修改對象或者數組的值無法改變視圖的問題
這篇文章主要介紹了react使用useState修改對象或者數組的值無法改變視圖的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08

