1. 程式人生 > >React中setState學習總結

React中setState學習總結

react中setState方法到底是非同步還是同步,其實這個是分在什麼條件下是非同步或者同步。

1.先來回顧一下react元件中改變state的幾種方式:

import React, { Component } from 'react'

class Index extends Component {
    state={
        count:1
    }
    test1 = () => {
        // 通過回撥函式的形式
        this.setState((state,props)=>({
            count:state.count+1
        }));
        console.log('test1 setState()之後',this.state.count);
    }
    test2 = () => {
        // 通過物件的方式(注意:此方法多次設定會合並且只調用一次!)
        this.setState({
            count:this.state.count+1
        });
        console.log('test2 setState()之後',this.state.count);
    }
    test3 = () => {
        // 不能直接修改state的值,此方法強烈不建議!!!因為不會觸發重新render
        this.state.count += 1;
    }
    test4 = () => {
        // 在第二個callback拿到更新後的state
        this.setState({
            count:this.state.count+1
        },()=>{// 在狀態更新且頁面更新(render)後執行
            console.log('test4 setState()之後',this.state.count);
        });
    }
    render() {
        console.log('render');
        return (
            <div>
                <h1>currentState:{this.state.count}</h1>
                <button onClick={this.test1}>測試1</button>
                <button onClick={this.test2}>測試2</button>
                <button onClick={this.test3} style={{color:'red'}}>測試3</button>
                <button onClick={this.test4}>測試4</button>
            </div>
        )
    }
}
export default Index;

2.setState()更新狀態是非同步還是同步:

需要判斷執行setState的位置

同步:在react控制的回撥函式中:生命週期鉤子/react事件監聽回撥

import React, { Component } from 'react'

class Index extends Component {
    state={
        count:1
    }
    /* 
    react事件監聽回撥中,setState()是非同步狀態
    */
    update1 = () => {
        console.log('update1 setState()之前',this.state.count);
        this.setState((state,props)=>({
            count:state.count+1
        }));
        console.log('update1 setState()之後',this.state.count);
    }
    /* 
    react生命週期鉤子中,setState()是非同步更新狀態
    */
    componentDidMount() {
        console.log('componentDidMount setState()之前',this.state.count);
        this.setState((state,props)=>({
            count:state.count+1
        }));
        console.log('componentDidMount setState()之後',this.state.count);
    }
    
    render() {
        console.log('render');
        return (
            <div>
                <h1>currentState:{this.state.count}</h1>
                <button onClick={this.update1}>測試1</button>
                <button onClick={this.update2}>測試2</button>
            </div>
        )
    }
}
export default Index;

非同步:非react控制的非同步回撥函式中:定時器回撥/原生事件監聽回撥/Promise

import React, { Component } from 'react'

class Index extends Component {
    state={
        count:1
    }
    /* 
    定時器回撥
    */
    update1 = () => {
        setTimeout(()=>{
            console.log('setTimeout setState()之前',this.state.count);//1
            this.setState((state,props)=>({
                count:state.count+1
            }));
            console.log('setTimeout setState()之後',this.state.count);//2
        });
    }
    /* 
    原生事件回撥
    */
    update2 = () => {
        const h1 = this.refs.count;
        h1.onclick = () => {
            console.log('onClick setState()之前',this.state.count);//1
            this.setState((state,props)=>({
                count:state.count+1
            }));
            console.log('onClick setState()之後',this.state.count);//2
        }
    }
    /* 
    Promise回撥
    */
    update3 = () => {
        Promise.resolve().then(value=>{
            console.log('Promise setState()之前',this.state.count);//1
            this.setState((state,props)=>({
                count:state.count+1
            }));
            console.log('Promise setState()之後',this.state.count);//2
        });
    }
    
    render() {
        console.log('render');
        return (
            <div>
                <h1 ref='count'>currentState:{this.state.count}</h1>
                <button onClick={this.update1}>測試1</button>
                <button onClick={this.update2}>測試2</button>
                <button onClick={this.update3}>測試3</button>
            </div>
        )
    }
}
export default Index;

3.setState()多次呼叫的問題:

非同步的setState()

(1)多次呼叫,處理方法:

setState({}):合併更新一次狀態,只調用一次render()更新介面,多次呼叫會合併為一個,後面的值會覆蓋前面的值。

setState(fn):更新多次狀態,只調用一次render()更新介面,多次呼叫不會合併為一個,後面的值會覆蓋前面的值。

import React, { Component } from 'react'

class Index extends Component {
    state={
        count:1
    }
    update1 = () => {
        console.log('update1 setState()之前',this.state.count);
        this.setState((state,props)=>({
            count:state.count+1
        }));
        console.log('update1 setState()之後',this.state.count);
        console.log('update1 setState()之前2',this.state.count);
        this.setState((state,props)=>({
            count:state.count+1
        }));
        console.log('update1 setState()之後2',this.state.count);
    }
    update2 = () => {
        console.log('update2 setState()之前',this.state.count);
        this.setState({
            count:this.state.count+1
        });
        console.log('update2 setState()之後',this.state.count);
        console.log('update2 setState()之前2',this.state.count);
        this.setState({
            count:this.state.count+1
        });
        console.log('update2 setState()之後2',this.state.count);
    }
    update3 = () => {
        console.log('update3 setState()之前',this.state.count);
        this.setState({
            count:this.state.count+1
        });
        console.log('update3 setState()之後',this.state.count);
        console.log('update3 setState()之前2',this.state.count);
        this.setState((state,props)=>({
            count:state.count+1
        }));// 這裡需要注意setState傳參為函式模式時,state會確保拿到的是最新的值
        console.log('update3 setState()之後2',this.state.count);
    }
    update4 = () => {
        console.log('update4 setState()之前',this.state.count);
        this.setState((state,props)=>({
            count:state.count+1
        }));
        console.log('update4 setState()之後',this.state.count);
        console.log('update4 setState()之前2',this.state.count);
        this.setState({
            count:this.state.count+1
        });// 這裡需要注意的是如果setState傳參為物件且在最後,那麼會與之前的setState合併
        console.log('update4 setState()之後2',this.state.count);
    }
    render() {
        console.log('render');
        return (
            <div>
                <h1>currentState:{this.state.count}</h1>
                <button onClick={this.update1}>測試1</button>
                <button onClick={this.update2}>測試2</button>
                <button onClick={this.update3}>測試3</button>
                <button onClick={this.update4}>測試4</button>
            </div>
        )
    }
}
export default Index;

(2)如何得到setState非同步更新後的狀態資料:

在setState()的callback回撥函式中

4.react中常見的setState面試題(setState執行順序)

import React, { Component } from 'react'
// setState執行順序
class Index extends Component {
    state={
        count:0
    }
    componentDidMount() {
        this.setState({count:this.state.count+1});
        this.setState({count:this.state.count+1});
        console.log(this.state.count);// 2 => 0
        this.setState(state=>({count:state.count+1}));
        this.setState(state=>({count:state.count+1}));
        console.log(this.state.count);// 3 => 0
        setTimeout(() => {
            this.setState({count:this.state.count+1});
            console.log('setTimeout',this.state.count);// 10 => 6
            this.setState({count:this.state.count+1});
            console.log('setTimeout',this.state.count);// 12 => 7
        });
        Promise.resolve().then(value=>{
            this.setState({count:this.state.count+1});
            console.log('Promise',this.state.count);// 6 => 4
            this.setState({count:this.state.count+1});
            console.log('Promise',this.state.count);// 8 => 5
        });
    }
    render() {
        console.log('render',this.state.count);// 1 => 0  // 4 => 3 // 5 => 4 // 7 => 5 // 9 => 6 // 11 => 7
        return (
            <div>
                <h1>currentState:{this.state.count}</h1>
                <button onClick={this.update1}>測試1</button>
                <button onClick={this.update2}>測試2</button>
                <button onClick={this.update3}>測試3</button>
                <button onClick={this.update4}>測試4</button>
            </div>
        )
    }
}
export default Index;

總結:react中setState()更新狀態的2種寫法

1)setState(updater,[callback])

updater:為返回stateChange物件的函式:(state,props)=>stateChange,接收的state和props都保證為最新的

2)setState(stateChange,[callback])

stateChange為物件,callback是可選的回撥函式,在狀態更新且介面更新後才執行

注意:

物件是函式方式的簡寫方式

如果新狀態不依賴於原狀態,則使用物件方式;

如果新狀態依賴於原狀態,則使用函式方式;

如果需要在setState()後獲取最新的狀態資料,在第二個callback函式中