为什么react中的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
48
49
50
import React, { useState } from "react";
/**
 * 两种写法里,setTimeout 取到的都是旧值,是因为 react 中一直遵循一个原则,
 * 即 state 指向的内容是不可变的,每一次 state 的更新都是指向变了,而原来指向的内容
 * 因为闭包的原因,依然被 setTimeout 使用着,没有释放
 *
 * 所以说 组件的每一次 渲染都是那一瞬间的状态
 */
function Example2() {
  const [count, setCount] = useState(0);
  function handleAlertClick() {
    setTimeout(() => {
      alert("You clicked on: " + count);
    }, 3000);
  }

  return (
    <div>
      <p><center>[wp_ad_camp_1]</center></p><p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={handleAlertClick}>Show alert</button>
    </div>
  );
}
class Example extends React.Component {
  state = {
    count: 0
  };
  setCount = count => {
    this.setState(count);
  };
  handleAlertClick = () => {
    setTimeout(() => {
      alert("You clicked on: " + this.state.count);
    }, 3000);
  };
  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setCount(this.state.count + 1)}>
          Click me
        </button>
        <button onClick={this.handleAlertClick}>Show alert</button>
      </div>
    );
  }
}

export default Example;

解决办法

使用 ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Example() {
  const [count, setCount] = useState(0);
  let ref = useRef();
  ref.current = count;
  function handleAlertClick() {
    setTimeout(() => {
      alert("You clicked on: " + ref.current);
    }, 3000);
  }

  return (
    <div>
      <p><center>[wp_ad_camp_2]</center></p><p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={handleAlertClick}>Show alert</button>
    </div>
  );
}