激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - React - 詳解react setState

詳解react setState

2022-02-27 17:14一個前端王 React

這篇文章主要介紹了react setState的相關資料,幫助大家更好的理解和學習使用react,感興趣的朋友可以了解下

setState是同步還是異步

自定義合成事件和react鉤子函數中異步更新state

以在自定義click事件中的setState為例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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是多少:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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中同步更新state

以在setTimeout中setState為例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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第一個參數換為函數:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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中一致,也是同步的。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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;

setState相關源碼

如下代碼均來自react17.0.2版本

目錄 ./packages/react/src/ReactBaseClasses.js

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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實例。

?
1
2
3
4
5
6
7
8
enqueueSetState: function(
  publicInstance,
  partialState,
  callback,
  callerName,
) {
  warnNoop(publicInstance, 'setState');
}

接著看warnNoop方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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;
    }
  }
}

看到這里,發現這個方法是將此次更新的update加入到更新隊列中,而在這個版本中并沒有發現isBatchingUpdates這個屬性的出現。貌似React Fiber改動還挺大,暫時先寫到這里,如果有新的發現會補充到這里。

總結

  • 自定義合成事件和react鉤子函數中異步更新state
  • 原生事件和setTimeout中同步更新state

以上就是詳解react setState的詳細內容,更多關于react setState的資料請關注服務器之家其它相關文章!

原文鏈接:https://juejin.cn/post/6948979480358551583

延伸 · 閱讀

精彩推薦
  • React聊一聊我對 React Context 的理解以及應用

    聊一聊我對 React Context 的理解以及應用

    這篇文章主要介紹了聊一聊我對 React Context 的理解以及應用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的...

    張國鈺6502022-02-24
  • React詳解react setState

    詳解react setState

    這篇文章主要介紹了react setState的相關資料,幫助大家更好的理解和學習使用react,感興趣的朋友可以了解下...

    一個前端王4922022-02-27
  • ReactReact實現登錄表單的示例代碼

    React實現登錄表單的示例代碼

    這篇文章主要介紹了React實現登錄表單的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下...

    喬路非6832022-02-23
  • React從框架作者角度聊:React調度算法的迭代過程

    從框架作者角度聊:React調度算法的迭代過程

    React內部最難理解的地方就是「調度算法」,不僅抽象、復雜,還重構了一次。可以說,只有React團隊自己才能完全理解這套算法。既然這樣,那本文嘗試從...

    魔術師卡頌8172022-01-10
  • React詳解對于React結合Antd的Form組件實現登錄功能

    詳解對于React結合Antd的Form組件實現登錄功能

    這篇文章主要介紹了詳解對于React結合Antd的Form組件實現登錄功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    浮生離夢6512022-02-23
  • React基于 Vite 的組件文檔編寫神器,又快又省心

    基于 Vite 的組件文檔編寫神器,又快又省心

    現在 Vite 的生態逐漸完善,今天給大家介紹一款 React 的組件/應用文檔編寫神器:vite-plugin-react-pages....

    前端星辰5072022-01-04
  • ReactReact.Children的用法詳解

    React.Children的用法詳解

    這篇文章主要介紹了React.Children的用法詳解,幫助大家更好的理解和學習使用React框架,感興趣的朋友可以了解下...

    uuihoo10672022-02-23
  • ReactReact事件機制源碼解析

    React事件機制源碼解析

    這篇文章主要介紹了React事件機制源碼解析的相關資料,幫助大家更好的理解和學習使用React框架,感興趣的朋友可以了解下...

    ZHANGYU10732022-02-25
主站蜘蛛池模板: av电影在线观看免费 | 蜜桃精品视频 | 久久亚洲精品视频 | 日韩精品久久久久久久九岛 | 中文字幕伦乱 | 国产亚洲精品久久久久久久软件 | 中文字幕亚洲一区二区三区 | 午夜精品老牛av一区二区三区 | 中国一级无毛黄色 | 精品欧美一区二区精品久久 | 小视频免费在线观看 | 久久人人97超碰国产公开结果 | 国产中出视频 | 国产一区免费在线 | 五月婷六月丁香狠狠躁狠狠爱 | 精品在线观看一区 | 香蕉视频99 | 天天色综合2 | 二区三区四区 | 蜜桃久久一区二区三区 | 一级免费看片 | 欧美亚洲国产日韩 | 国产精品午夜未成人免费观看 | 5xsq在线视频 | a级欧美 | 51国产偷自视频区视频小蝌蚪 | 欧美a在线观看 | 福利在线国产 | 免费亚洲视频在线观看 | 91精品中文字幕 | 加勒比综合 | 91看片成人 | 免费观看一区二区三区视频 | 俄罗斯论理片 | 久久蜜桃精品一区二区三区综合网 | 欧美一级做一级爱a做片性 91在线视频观看 | 精品一区二区免费视频视频 | 欧美激情天堂 | 亚洲国产视频在线 | 国产成人精品无人区一区 | 日韩午夜一区二区三区 |