摘要:的批量更新優(yōu)化也是建立在異步合成事件鉤子函數(shù)之上的,在原生事件和中不會批量更新,在異步中如果對同一個值進(jìn)行多次,的批量更新策略會對其進(jìn)行覆蓋,取最后一次的執(zhí)行,如果是同時多個不同的值,在更新時會對其進(jìn)行合并批量更新。
api解析: setState(updater, [callback])
updater: 更新數(shù)據(jù) FUNCTION/OBJECT callback: 更新成功后的回調(diào) FUNCTION
// updater - Function this.setState((prevState, props) => { return {counter: prevState.counter + props.step}; }); // update - Object this.setState({quantity: 2})setState的特點:
1.異步:react通常會集齊一批需要更新的組件,然后一次性更新來保證渲染的性能 2.淺合并 Objecr.assign()setState問題與解決
在使用setState改變狀態(tài)之后,立刻通過this.state去拿最新的狀態(tài)
解決: componentDidUpdate或者setState的回調(diào)函數(shù)里獲取
// setState回調(diào)函數(shù) changeTitle: function (event) { this.setState({ title: event.target.value }, () => this.APICallFunction()); }, APICallFunction: function () { // Call API with the updated value }
有一個需求,需要在在onClick里累加兩次,使用對象的方法更新,則只會加一次
解決: 使用updater function
onClick = () => { this.setState({ index: this.state.index + 1 }); this.setState({ index: this.state.index + 1 }); } // 最后解析為,后面的數(shù)據(jù)會覆蓋前面的更改,所以最終只加了一次. Object.assign( previousState, {index: state.index+ 1}, {index: state.index+ 1}, ) //正確寫法 onClick = () => { this.setState((prevState, props) => { return {quantity: prevState.quantity + 1}; }); this.setState((prevState, props) => { return {quantity: prevState.quantity + 1}; }); }注意:
1.不要在render()函數(shù)里面寫setstate(),除非你自己定制了shouldComponentUpdate方法,要不然會引起無限循環(huán)
render() { //this.setState return( //...dom ) }
2.為什么不能使用第一個方式更新 react為了實現(xiàn)高效render, state其實是一個隊列,setState是將數(shù)據(jù)插入隊列中,使用第一種不會觸發(fā)渲染, react提供了setState的實例方法可以觸發(fā)render,可以看后面的源碼
// 1 this.state.num = 1 // 2 this.setState({ num: this.state.num + 1 })
3.對數(shù)組和對象等引用對象操作時,使用返回新對象的方法 array: 不要使用push、pop、shift、unshift、splice可使用concat、slice、filter、擴(kuò)展語法 object: Object.assgin/擴(kuò)展語法setState更新機(jī)制
如圖: pending queue 和 update queue
setState源碼 this.setState()ReactComponent.prototype.setState = function (partialState, callback) { // 將setState事務(wù)放進(jìn)隊列中 // this.updater就是ReactUpdateQueue, this是組件的實例 this.updater.enqueueSetState(this, partialState); if (callback) { this.updater.enqueueCallback(this, callback, "setState"); } };enqueueSetState()
enqueueSetState: function (publicInstance, partialState) { // 獲取當(dāng)前組件的instance // 實例中有兩個非常重要的屬性:_pendingStateQueue(待更新隊列) 與 _pendingCallbacks(更新回調(diào)隊列) var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, "setState"); // 初始化待更新隊列 var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []); // 將要更新的state放入一個數(shù)組里 queue.push(partialState); // 將要更新的component instance也放在一個隊列里 enqueueUpdate(internalInstance); }enqueueUpdate
當(dāng)前如果正處于創(chuàng)建/更新組件的過程,就不會立刻去更新組件,而是先把當(dāng)前的組件放在dirtyComponent里,所以不是每一次的setState都會更新組件
function enqueueUpdate(component) { // 如果沒有處于批量創(chuàng)建/更新組件的階段,則處理update state事務(wù) if (!batchingStrategy.isBatchingUpdates) { batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } // 如果正處于批量創(chuàng)建/更新組件的過程,將當(dāng)前的組件放在dirtyComponents數(shù)組中 dirtyComponents.push(component); }batchingStrategy
var ReactDefaultBatchingStrategy = { // 用于標(biāo)記當(dāng)前是否出于批量更新 isBatchingUpdates: false, // 當(dāng)調(diào)用這個方法時,正式開始批量更新 batchedUpdates: function (callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // 如果當(dāng)前事務(wù)正在更新過程在中,則調(diào)用callback,既enqueueUpdate if (alreadyBatchingUpdates) { return callback(a, b, c, d, e); } else { // 否則執(zhí)行更新事務(wù) return transaction.perform(callback, null, a, b, c, d, e); } } };transaction
initalize(空函數(shù)) -> perform(anyMethos) -> close
// 將isBatchingUpdates置為false var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function () { ReactDefaultBatchingStrategy.isBatchingUpdates = false; } }; // 循環(huán)所有dirtyComponent,調(diào)用updateComponent來執(zhí)行所有的生命周期方法,componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate 最后實現(xiàn)組件的更新 var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates) }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];setState面試
面試官:“react中setState是同步的還是異步?” 我:“異步的,setState不能立馬拿到結(jié)果。” 面試官:“那什么場景下是異步的,可不可能是同步,什么場景下又是同步的?”
下題結(jié)果是:
class App extends React.Component { state = { val: 0 } componentDidMount() { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) this.setState({ val: this.state.val + 1 }) console.log(this.state.val) setTimeout(_ => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val); this.setState({ val: this.state.val + 1 }) console.log(this.state.val) }, 0) } render() { return總結(jié){this.state.val}} } // 結(jié)果就為 0, 0, 2, 3
1.setState 只在合成事件和鉤子函數(shù)中是“異步”的,在原生事件和 setTimeout 中都是同步的。 2.setState的“異步”并不是說內(nèi)部由異步代碼實現(xiàn),其實本身執(zhí)行的過程和代碼都是同步的,只是合成事件和鉤子函數(shù)的調(diào)用順序在更新之前,導(dǎo)致在合成事件和鉤子函數(shù)中沒法立馬拿到更新后的值,形式了所謂的“異步”,當(dāng)然可以通過第二個參數(shù) setState(partialState, callback) 中的callback拿到更新后的結(jié)果。 3.setState 的批量更新優(yōu)化也是建立在“異步”(合成事件、鉤子函數(shù))之上的,在原生事件和setTimeout 中不會批量更新,在“異步”中如果對同一個值進(jìn)行多次 setState , setState 的批量更新策略會對其進(jìn)行覆蓋,取最后一次的執(zhí)行,如果是同時 setState 多個不同的值,在更新時會對其進(jìn)行合并批量更新。1、合成事件中的setState
合成事件,react為了解決跨平臺,兼容性問題,自己封裝了一套事件機(jī)制,代理了原生的事件,像在jsx中常見的onClick、onChange這些都是合成事件 合成事件中也有batchedUpdates方法,是通過同樣的事務(wù)完成的
class App extends Component { state = { val: 0 } increment = () => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 輸出的是更新前的val --> 0 } render() { return (2、生命周期函數(shù)中的setState{`Counter is: ${this.state.val}`}) } }
整個生命周期中就是一個事物操作,所以標(biāo)識位isBatchingUpdates = true,所以流程到了enqueueUpdate()時,實例對象都會加入到dirtyComponents 數(shù)組中
class App extends Component { state = { val: 0 } componentDidMount() { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 輸出的還是更新前的值 --> 0 } render() { return (3、原生事件中的setState{`Counter is: ${this.state.val}`}) } }
原生事件是指非react合成事件,原生自帶的事件監(jiān)聽 addEventListener ,或者也可以用原生js、jq直接 document.querySelector().onclick 這種綁定事件的形式都屬于原生事件 原生事件綁定不會通過合成事件的方式處理,自然也不會進(jìn)入更新事務(wù)的處理流程。setTimeout也一樣,在setTimeout回調(diào)執(zhí)行時已經(jīng)完成了原更新組件流程,不會放入dirtyComponent進(jìn)行異步更新,其結(jié)果自然是同步的。
class App extends Component { state = { val: 0 } changeValue = () => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 輸出的是更新后的值 --> 1 } componentDidMount() { document.body.addEventListener("click", this.changeValue, false) } render() { return (4、setTimeout中的setState{`Counter is: ${this.state.val}`}) } }
基于event loop的模型下, setTimeout 中里去 setState 總能拿到最新的state值。
class App extends Component { state = { val: 0 } componentDidMount() { setTimeout(_ => { this.setState({ val: this.state.val + 1 }) console.log(this.state.val) // 輸出更新后的值 --> 1 }, 0) } render() { return (5、批量更新{`Counter is: ${this.state.val}`}) } }
在 setState 的時候react內(nèi)部會創(chuàng)建一個 updateQueue ,通過 firstUpdate 、 lastUpdate 、 lastUpdate.next 去維護(hù)一個更新的隊列,在最終的 performWork 中,相同的key會被覆蓋,只會對最后一次的 setState 進(jìn)行更新
class App extends Component { state = { val: 0 } batchUpdates = () => { this.setState({ val: this.state.val + 1 }) this.setState({ val: this.state.val + 1 }) this.setState({ val: this.state.val + 1 }) } render() { return ({`Counter is ${this.state.val}`} // 1) } }
引發(fā)感想來源:
https://juejin.im/post/5b45c5...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98697.html
摘要:根本原因在于,并不是真正意義上的異步操作,它只是模擬了異步的行為。而合成事件和生命周期函數(shù)中,是受控制的,其會將設(shè)置為,從而走的是類似異步的那一套。總結(jié)此處總結(jié)是直接引用了只在合成事件和鉤子函數(shù)中是異步的,在原生事件和中都是同步的。 如何使用setState 在 React 日常的使用中,一個很重要的點就是,不要直接去修改 state。例如:this.state.count = 1是無...
摘要:因為是深入系列文章,本文不會仔細(xì)介紹每個生命周期方法的使用,而是會重點講解在使用組件生命周期時,經(jīng)常遇到的疑問和錯誤使用方式。父組件發(fā)生更新導(dǎo)致的組件更新,生命周期方法的調(diào)用情況同上所述。 文:徐超,《React進(jìn)階之路》作者授權(quán)發(fā)布,轉(zhuǎn)載請注明作者及出處 React 深入系列4:組件的生命周期 React 深入系列,深入講解了React中的重點概念、特性和模式等,旨在幫助大家加深...
摘要:當(dāng)真正執(zhí)行狀態(tài)修改時,依賴的并不能保證是最新的,因為會把多次的修改合并成一次,這時,還是等于這幾次修改發(fā)生前的。下篇預(yù)告深入系列組件的生命周期新書推薦進(jìn)階之路作者徐超畢業(yè)于浙江大學(xué),碩士,資深前端工程師,長期就職于能源物聯(lián)網(wǎng)公司遠(yuǎn)景智能。 文:徐超,《React進(jìn)階之路》作者授權(quán)發(fā)布,轉(zhuǎn)載請注明作者及出處 React 深入系列3:Props 和 State React 深入系列,深...
簡介:簡單實現(xiàn)react-redux基礎(chǔ)api react-redux api回顧 把store放在context里,所有子組件可以直接拿到store數(shù)據(jù) 使組件層級中的 connect() 方法都能夠獲得 Redux store 根組件應(yīng)該嵌套在 中 ReactDOM.render( , rootEl ) ReactDOM.render( ...
摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實現(xiàn)文件分片斷點續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡單的方式。插....
閱讀 3471·2023-04-25 18:52
閱讀 2484·2021-11-22 15:31
閱讀 1224·2021-10-22 09:54
閱讀 3010·2021-09-29 09:42
閱讀 605·2021-09-26 09:55
閱讀 909·2021-09-13 10:28
閱讀 1098·2019-08-30 15:56
閱讀 2107·2019-08-30 15:55