Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[React] React Hooks 中使用 setTimeout #20

Open
cheungseol opened this issue May 29, 2019 · 1 comment
Open

[React] React Hooks 中使用 setTimeout #20

cheungseol opened this issue May 29, 2019 · 1 comment
Labels

Comments

@cheungseol
Copy link
Owner

cheungseol commented May 29, 2019

问题

在hooks 中使用 setTimeout 方法,方法中访问到的函数 state 始终是初始值,而不是更新后的最新 state

demo

在这个例子中,首先执行setCount 将 count 设为 5, 然后经过 3 秒后执行 setCountInTimeout, 将 countInTimeout 的值设置为count 的值

我们最初期望的是 这时候 countInTimeout 就等于 此刻 count 最新的值 5, 然而 countInTimeout 却保持了最开始的 count 值 0

import React, { useEffect, useState } from 'react';

const TimeoutExample = () => {
  const [count, setCount] = useState(0);
  const [countInTimeout, setCountInTimeout] = useState(0);

  useEffect(() => {
    setTimeout(() => {
      setCountInTimeout(count); // count is 0 here
    }, 3000);
    setCount(5); // Update count to be 5 after timeout is scheduled
  }, []);

  return (
    <div>
      Count: {count}
      <br />
      setTimeout Count: {countInTimeout}
    </div>
  );
};

export default TimeoutExample;

原因

setTimeout 是一个闭包,setTimeout 函数执行的时候使用的参数 count 读取自setTimeout 函数创建的时候,即 0。 setTimeout 使用闭包的方式异步访问 count 的值。当整个函数组件re-render的时候,会创建出一个新的 setTimeout 函数一个新的闭包,但并没有改变最初封装它的那个闭包的值

作者也提到这么设计的初衷是满足这样的场景:比如订阅了一个ID,当随后需要取消订阅的时候,避免ID发生变化而造成不能取消订阅的问题

解决方法

使用一个 container 来把最新的 state 也就是 count 的值穿进去,并在随后的 timeout 函数中读取最新的 state 。

可以使用 useRef。 通过 ref's current 来同步最新的 state, 然后在 timeout 函数中读取 current 的值。使用 ref 在异步callback函数中访问最新的 当前的 state

const countRef = useRef(count);
countRef.current = count;

const getCountTimeout = () => {
  setTimeout(() => {
    setTimeoutCount(countRef.current);
  }, 2000);
};

参考
State from useState hook inside a setTimeout is not updated

@jim-kk-hc
Copy link

useCallback deps 加上 count?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants