您好,登錄后才能下訂單哦!
setState如何在react 中使用?針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
以在自定義click事件中的setState為例
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 }; } handleClick = () => { this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); console.log(this.state.count); } render() { return ( <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}> {this.state.count} </div> ) } } export default Test;
點擊一次,最終this.state.count的打印結果是1,頁面展示的是2。通過現象看,三次setState只是最后一次setState生效了,前兩次都setState無效果。因為假如把第一次setState改為+3,count打印結果為1,展示結果為2,沒有發生變化。而且沒有同步獲得count的結果。
此時,我們可以調整代碼,通過setState的第二個參數,來獲得更新后的state:
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 }; } handleClick = () => { this.setState({ count: this.state.count + 3 }, () => { console.log('1', this.state.count) }); this.setState({ count: this.state.count + 1 }, () => { console.log('2', this.state.count); }); this.setState({ count: this.state.count + 1 }, () => { console.log('3', this.state.count); }); console.log(this.state.count); } render() { return ( <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}> {this.state.count} </div> ) } } export default Test;
此時,點擊一次,三個setState的回調函數中,打印結果分別是。
1
1: 2
2: 2
3: 2
首先,最后一行直接打印1。然后,在setState的回調中,打印出的結果都是最新更新的2。雖然前兩次setState未生效,但是它們第二個參數中還是會打印出2。
此時將setState的第一個參數換成函數,通過函數的第一個參數可以獲得更新前的state。
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 }; } handleClick = () => { this.setState((prevState, props) => { return { count: prevState.count + 1 } }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }); console.log(this.state.count); } render() { return ( <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}> {this.state.count} </div> ) } } export default Test;
此時,打印出的結果為1,但是頁面展示出來的count為4。可以發現,如果setState以傳參的方式去更新state,幾次setState并不會只更新最后一次,而是幾次更新state都會生效。
接下來看下第二個函數中打印的count是多少:
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 }; } handleClick = () => { this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('1', this.state.count); }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('2', this.state.count); }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('3', this.state.count); }); console.log(this.state.count); } render() { return ( <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}> {this.state.count} </div> ) } } export default Test;
此時,點擊一次,三個setState的回調函數中,打印結果如下,可想而知,頁面的展示結果也為4
1
1: 4
2: 4
3: 4
將上邊代碼放入如componentDidMount中,輸出結果跟上邊一致。
因為,可以得知,在自定義合成事件和鉤子函數中,state的更新是異步的。
以在setTimeout中setState為例
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 }; } componentDidMount() { setTimeout(() => { this.setState({ count: this.state.count + 1 }, () => { console.log('1:', this.state.count); }); this.setState({ count: this.state.count + 1 }, () => { console.log('2:', this.state.count); }); this.setState({ count: this.state.count + 1 }, () => { console.log('3:', this.state.count); }); console.log(this.state.count); }, 0); } render() { return ( <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }}> {this.state.count} </div> ) } } export default Test;
此時,打印出的結果如下:
1: 2
2: 3
3: 4
4
將setState第一個參數換為函數:
componentDidMount() { setTimeout(() => { this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('1', this.state.count); }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('2', this.state.count); }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('3', this.state.count); }); console.log(this.state.count); }, 0); }
打印出的結果和上邊一致。
是不是有一種state完全可控的感覺,在setTimeout中,多次setState都會生效,而且在每一個setState的第二個參數中都可以得到更新后的state。
同樣地,在原生事件中輸出地結果和setTimeout中一致,也是同步的。
import React, { Component } from 'react'; class Test extends Component { constructor(props) { super(props); this.state = { count: 1 }; } componentDidMount() { document.body.addEventListener('click', this.handleClick, false); } componentWillUnmount() { document.body.removeEventListener('click', this.handleClick, false); } handleClick = () => { this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('1', this.state.count); }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('2', this.state.count); }); this.setState((prevState, props) => { return { count: prevState.count + 1 } }, () => { console.log('3', this.state.count); }); console.log(this.state.count); } render() { return ( <div style={{ width: '100px', height: '100px', backgroundColor: "yellow" }} > {this.state.count} </div> ) } } export default Test;
如下代碼均來自react17.0.2版本
目錄 ./packages/react/src/ReactBaseClasses.js
function Component(props, context, updater) { this.props = props; this.context = context; // If a component has string refs, we will assign a different object later. this.refs = emptyObject; // We initialize the default updater but the real one gets injected by the // renderer. this.updater = updater || ReactNoopUpdateQueue; } Component.prototype.isReactComponent = {}; Component.prototype.setState = function(partialState, callback) { invariant( typeof partialState === 'object' || typeof partialState === 'function' || partialState == null, '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'); };
setState可以接收兩個參數,第一個參數可以是object,function,和null,undefined,就不會拋出錯誤。執行下邊的this.updater.enqueueSetState方法。全局查找enqueueSetState,找到兩組目錄下有這個變量。
首先是第一組目錄:
目錄 ./packages/react/src/ReactNoopUpdateQueue.js 第100行enqueueSetState方法,參數分別為this,初始化state,回調,和字符串setState,this是指當前React實例。
enqueueSetState: function( publicInstance, partialState, callback, callerName, ) { warnNoop(publicInstance, 'setState'); }
接著看warnNoop方法:
const didWarnStateUpdateForUnmountedComponent = {}; function warnNoop(publicInstance, callerName) { if (__DEV__) { const constructor = publicInstance.constructor; const componentName = (constructor && (constructor.displayName || constructor.name)) || 'ReactClass'; const warningKey = `${componentName}.${callerName}`; if (didWarnStateUpdateForUnmountedComponent[warningKey]) { return; } console.error( "Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = {};` ' + 'class property with the desired state in the %s component.', callerName, componentName, ); didWarnStateUpdateForUnmountedComponent[warningKey] = true; } }
這段代碼相當于給didWarnStateUpdateForUnmountedComponent對象中加入屬性,屬性的key為React 當前要setState的組件.setState,如果當前有這個屬性則返回;如果當前沒這個屬性或者這個屬性值為false,則設置這個屬性的值為true。
再去看另外一個目錄:
目錄 ./react-reconciler/src/ReactFiberClassComponent.new.js和ReactFiberClassComponent.old.js
const classComponentUpdater = { enqueueSetState(inst, payload, callback) { const fiber = getInstance(inst); const eventTime = requestEventTime(); const lane = requestUpdateLane(fiber); const update = createUpdate(eventTime, lane); update.payload = payload; if (callback !== undefined && callback !== null) { if (__DEV__) { warnOnInvalidCallback(callback, 'setState'); } update.callback = callback; } enqueueUpdate(fiber, update, lane); const root = scheduleUpdateOnFiber(fiber, lane, eventTime); if (root !== null) { entangleTransitions(root, fiber, lane); } if (__DEV__) { if (enableDebugTracing) { if (fiber.mode & DebugTracingMode) { const name = getComponentNameFromFiber(fiber) || 'Unknown'; logStateUpdateScheduled(name, lane, payload); } } } if (enableSchedulingProfiler) { markStateUpdateScheduled(fiber, lane); } } }
其中主要看 enqueueUpdate 這個函數
目錄 ./react-reconciler/src/ReactUpdateQueue.new.js和ReactUpdateQueue.old.js
export function enqueueUpdate<State>( fiber: Fiber, update: Update<State>, lane: Lane, ) { const updateQueue = fiber.updateQueue; if (updateQueue === null) { // Only occurs if the fiber has been unmounted. return; } const sharedQueue: SharedQueue<State> = (updateQueue: any).shared; if (isInterleavedUpdate(fiber, lane)) { const interleaved = sharedQueue.interleaved; if (interleaved === null) { // This is the first update. Create a circular list. update.next = update; // At the end of the current render, this queue's interleaved updates will // be transfered to the pending queue. pushInterleavedQueue(sharedQueue); } else { update.next = interleaved.next; interleaved.next = update; } sharedQueue.interleaved = update; } else { const pending = sharedQueue.pending; if (pending === null) { // This is the first update. Create a circular list. update.next = update; } else { update.next = pending.next; pending.next = update; } sharedQueue.pending = update; } if (__DEV__) { if ( currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate ) { console.error( 'An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.', ); didWarnUpdateInsideUpdate = true; } } }
關于setState如何在react 中使用問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。