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