react中如何正确使用setState(附例子)
概述
setState中對(duì)于某個(gè)state多次修改,只執(zhí)行一次(最后一次),所以可以將修改放在同一次中
import React, {Component} from 'react';
class Demotest extends Component {
constructor(props) {
super(props);
this.state = {
number: 1
};
}
componentDidMount() {
this.setState({ number: this.state.number + 1 });
}
addNumber(e) {
this.setState({ number: this.state.number + 1 });
this.setState({ number: this.state.number + 1 });
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
}
render() {
return (
<div>
<div>
<span>當(dāng)前數(shù)字是{this.state.number}</span>
</div>
<br/>
<div>
<button onClick={e => {
this.addNumber(e);
}}>點(diǎn)擊添加
</button>
</div>
</div>
);
}
}
export default Demotest;
初始加載后
這時(shí)發(fā)現(xiàn)頁面上顯示的是2,控制臺(tái)輸出的卻是1,按道理 componentDidMount?里的應(yīng)該已經(jīng)成功了,不然不會(huì)顯示2,那為什么控制臺(tái)輸出的卻是1 呢?
由于 setState?是異步的所以,所以同步代碼執(zhí)行結(jié)束后才會(huì)執(zhí)行,所以在 console.log('componentDidMount: ', this.state.number);? 執(zhí)行的時(shí)候 state?還沒有被改變,所以生命周期里的輸出還是原來的值。
此時(shí)我們點(diǎn)擊按鈕,觸發(fā)函數(shù) addNumber? 發(fā)現(xiàn),函數(shù)里的三次 setState?只生效了一次 ,頁面顯示的數(shù)字變成了3,控制臺(tái)輸出了**2?**(對(duì)應(yīng)上面代碼20行),這是因?yàn)槎啻胃卤缓喜ⅲ惒降脑驅(qū)е轮惠敵隽?。官方文檔上明確說明,如果希望通過這里的狀態(tài)更新一下個(gè)狀態(tài),需要在 setState?中使用函數(shù)來取得
addNumber(e) {
this.setState((nextState) => {
console.log(nextState.number);
return { number: nextState.number + 1 };
});
this.setState((nextState) => {
console.log(nextState.number);
return { number: nextState.number + 1 };
});
this.setState((nextState) => {
console.log(nextState.number);
return { number: nextState.number + 1 };
});
}
此時(shí)輸出的是2、3、4
這里要說明的是當(dāng)你在 ****willMount**?前設(shè)進(jìn)行 state?的設(shè)置不會(huì) render?的觸發(fā),而事件和可以 componentDidMount?觸發(fā) render?, 而且原生的事件可以優(yōu)先這個(gè)機(jī)制
componentDidMount() {
document.body.addEventListener('click', this.updateData, false);
}
updateData = () => {
this.setState({
number: this.state.number + 1
});
console.log('componentDidMount: ', this.state.number);
};
詳解setState
上面概述了下setState會(huì)出現(xiàn)的'合并',下面引用官網(wǎng)的一段話
setState()將對(duì)組件 state 的更改排入隊(duì)列,并通知 React 需要使用更新后的 state 重新渲染此組件及其子組件。這是用于更新用戶界面以響應(yīng)事件處理器和處理服務(wù)器數(shù)據(jù)的主要方式
將setState()視為_請(qǐng)求_而不是立即更新組件的命令。為了更好的感知性能,React 會(huì)延遲調(diào)用它,然后通過一次傳遞更新多個(gè)組件。React 并不會(huì)保證 state 的變更會(huì)立即生效。
setState()并不總是立即更新組件。它會(huì)批量推遲更新。這使得在調(diào)用setState()后立即讀取this.state成為了隱患。為了消除隱患,請(qǐng)使用componentDidUpdate或者setState的回調(diào)函數(shù)(setState(updater, callback)),這兩種方式都可以保證在應(yīng)用更新后觸發(fā)。如需基于之前的 state 來設(shè)置當(dāng)前的 state,請(qǐng)閱讀下述關(guān)于參數(shù)updater的內(nèi)容。
除非shouldComponentUpdate()返回false,否則setState()將始終執(zhí)行重新渲染操作。如果可變對(duì)象被使用,且無法在shouldComponentUpdate()中實(shí)現(xiàn)條件渲染,那么僅在新舊狀態(tài)不一時(shí)調(diào)用setState()可以避免不必要的重新渲染
下面通過幾個(gè)例子分析setState在實(shí)際應(yīng)用中的運(yùn)用和注意點(diǎn)
附上一段源碼
Component.prototype.setState = function(partialState, callback) {
// 校驗(yàn)是否符合三種情況
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
// 接受狀態(tài)變量的對(duì)象以進(jìn)行更新或者通過函數(shù)返回狀態(tài)變量的對(duì)象
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
enqueueSetState: function (publicInstance, partialState) {
if (process.env.NODE_ENV !== 'production') {
ReactInstrumentation.debugTool.onSetState();
process.env.NODE_ENV !== 'production' ? warning(partialState != null, 'setState(...): You passed an undefined or null state object; ' + 'instead, use forceUpdate().') : void 0;
}
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
if (!internalInstance) {
return;
}
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
enqueueUpdate(internalInstance);
}
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
function enqueueUpdate(component) {
ensureInjected();
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (This is called by each top-level update
// function, like setState, forceUpdate, etc.; creation and
// destruction of top-level components is guarded in ReactMount.)
// 是批處理更新, 默認(rèn)為false
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
setState的第一個(gè)參數(shù)
// 官網(wǎng)的api ---> 第一個(gè)參數(shù)可以是對(duì)象或者一個(gè)函數(shù)
setState(updater, [callback])
如果傳入的是對(duì)象則會(huì)淺層合并到新的 state 中,后調(diào)用的?setState()?將覆蓋同一周期內(nèi)先調(diào)用?setState?的值
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
相當(dāng)于同個(gè)屬性被合并。
如果第一個(gè)參數(shù)是函數(shù),形式如下
(state, props) => stateChange
函數(shù)中接收的?state?和?props?都保證為最新, 是你的上次setState的狀態(tài)值。
具體例子
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props)
this.state = {
number: 0
}
}
// 運(yùn)行一遍,不理解的對(duì)照注釋看
add () {
// 進(jìn)入隊(duì)列 number: 0 + 222 = 222
this.setState({
number: this.state.number + 222
})
// 進(jìn)入隊(duì)列 number: 0 + 5 =5
this.setState({
number: this.state.number + 5
})
// 進(jìn)入隊(duì)列 number: 5 + 1 = 6
this.setState((state, props) => {
// 此時(shí)state是上次setState中的number ==> 5
console.log('one', state);
return {
number: state.number + 1
}
})
// 進(jìn)入隊(duì)列 number: 0 + 1 = 1
this.setState({
number: this.state.number + 1
})
// 進(jìn)入隊(duì)列: 1 + 2 = 3
this.setState((state, props) => {
// 此時(shí)state是上次setState中的number ==> 1
console.log('two', state);
return {
number: state.number + 2
}
})
// 進(jìn)入隊(duì)列: 3 + 1 = 4
this.setState((state, props) => {
// 此時(shí)state是上次setState中的number ==> 3
console.log('three', state);
return {
number: state.number + 1
}
})
}
render () {
return (
<div>
<button onClick={e =>{this.add()}}>add</button>
{this.state.number}
</div>
);
}
}
export default App;
輸出分別為 5 、 1 、? 3,最后頁面顯示的是4。
第二個(gè)參數(shù)
setState()?的第二個(gè)參數(shù)為可選的回調(diào)函數(shù),它將在?setState?完成合并并重新渲染組件后執(zhí)行。通常,我們建議使用?componentDidUpdate()?來代替此方式。
考慮如下場(chǎng)景:
在同個(gè)組件中一個(gè)具有進(jìn)度條的頁面需要不停的加載請(qǐng)求,只有當(dāng)某次請(qǐng)求正確返回?cái)?shù)據(jù)后出現(xiàn)新的內(nèi)容區(qū)域。當(dāng)數(shù)據(jù)返回后你將進(jìn)度條比例設(shè)置為100,同時(shí)設(shè)置數(shù)據(jù)到state上。
this.ajax() {
this.$post(xxx.com)
.then(res => {
this.setState({
progress: 100,
data: res
})
})
.catch(err => {console.log(err)})
}
設(shè)置為100%是為了數(shù)據(jù)到來后進(jìn)度條立馬拉滿,然后在渲染對(duì)應(yīng)的數(shù)據(jù)內(nèi)容區(qū)域,這樣寫會(huì)出現(xiàn)進(jìn)度條其實(shí)也拉滿了,但是視覺效果沒出來數(shù)據(jù)內(nèi)容區(qū)域就出來的情況。所以用到第二個(gè)參數(shù)
this.ajax() {
this.$post(xxx.com)
.then(res => {
this.setState({
progress: 100,
}, () => {
this.setState({
data: res
})
})
})
.catch(err => {console.log(err)})
}
總結(jié)
以上是生活随笔為你收集整理的react中如何正确使用setState(附例子)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微软发布纪念视频,官宣正式收购动视暴雪
- 下一篇: java mysql驱动