当前位置: 首页 > 知识库问答 >
问题:

javascript - React useState异步代码没有更新?

姜钊
2024-03-07

背景:

React代码如下

import { useState } from 'react';export default function App() {  const [count, setCount] = useState(0);  const handleClick = () => {    setCount(count + 1);    setTimeout(() => {      console.log(count, 'end'); // 还是 0!    }, 5000);  }  return (    <>      <button onClick={handleClick}>+1</button>      <span>{count}</span>    </>  );}

setCount的更新是异步更新的,setTimeout也是异步的,为什么点击,console.log打印的是0,而不是1。

尝试:

尝试用普通js函数进行比较,js代码如下

function outerFunction() {      let count = 0;        function innerFunction() {        console.log(count);    }          count = 1;  // 将count更新为1    setTimeout(innerFunction, 5000);       count = 2; }      outerFunction();

console.log打印的是最新的值2。

疑问: 两段代码的打印结果不同,是什么原因呢?

共有4个答案

董同
2024-03-07

当你setState时,组件会rerender,所谓rerender其实就是App这个function重新执行了一次,重新执行时你通过useState这个hook取到是最新值,但是你上一次App的函数调用中的setTimeout的回调中的count因为闭包的关系访问的仍然是当时的值,转换成下面这种更接近:

var value;function useState(val) {    return [value ??= val, v => {        value = v;        App();    }]}function App() {        const [count, setCount] = useState(0);    const handleClick = () => {      setCount(count + 1);      setTimeout(() => {        console.log(count, 'end'); // 还是 0!      }, 5000);    }    console.log('render', count)    return {        onClick: handleClick,    }}    App().onClick()
翟曦之
2024-03-07

首先这里是const,不会改变的。是let的话也不会改变,除非是一个object,才可能会改变。

现在它可以输出1了。

import { useState } from 'react';function Count() {  const [count, setCount] = useState({ count: 0 });  const handleClick = () => {    setCount((pre) => {      pre.count += 1;      return pre;    });    setTimeout(() => {      console.log(count.count, 'end'); // 还是 0!    }, 5000);  };  return (    <>      <button onClick={handleClick}>+1</button>      <span>{count.count}</span>    </>  );}export default Count;
司浩壤
2024-03-07
setTimeout(() => {  console.log(count, 'end'); // 还是 0!}, 5000);

你可以把react组件的每次渲染理解为生成了一个平行宇宙,所以代码里面的定时器,是在上一个渲染生成的平行宇宙中,输出的count也是那个平行宇宙中的值。

漆雕誉
2024-03-07

在React中,useState的更新是异步的,这意味着当你调用setCount函数时,count的值不会立即改变。相反,React会等待当前的渲染周期完成,然后在下一个渲染周期中更新count的值。

在你的React代码示例中,当你点击按钮时,setCount函数会被调用,count的值会被计划在下一个渲染周期中更新。然后,setTimeout函数会在5秒后执行,此时count的值还没有被更新,所以console.log打印的仍然是旧的值0。

而在你的JavaScript代码示例中,当你调用count = 1count = 2时,count的值会立即改变,所以在5秒后执行innerFunction时,console.log打印的是最新的值2。

为了解决这个问题,你可以在setTimeout的回调函数中直接使用setCount函数来获取最新的count值。这样,无论count何时更新,setTimeout的回调函数中都能获取到最新的值。以下是修改后的React代码示例:

import { useState } from 'react';export default function App() {  const [count, setCount] = useState(0);  const handleClick = () => {    setCount(count + 1);    setTimeout(() => {      console.log(count, 'end'); // 使用setCount获取最新的值    }, 5000);  }  return (    <>      <button onClick={handleClick}>+1</button>      <span>{count}</span>    </>  );}

在这个修改后的示例中,setTimeout的回调函数中使用了count而不是setCount来获取最新的值。当count的值更新时,setTimeout的回调函数中也会获取到最新的值,因此console.log会打印出正确的值。

 类似资料:
  • 打印结果: 打印走到了else中,输出了123。但是没有走trycatch代码?不知道是什么原因?

  • 使用Mocha测试异步代码并不简单!只需在测试完成后调用回调。通过添加一个回调(通常命名done)it(),Mocha将知道它应该等待调用此函数来完成测试。此回调接受Error实例(或其子类)或伪值; 其他任何事情都会导致测试失败。 describe('User', function() { describe('#save()', function() { it('should save wi

  • 异步代码是指当调用后不会立即完成的代码。 setTimeout(() => { console.log('do stuff'); }, 3000 ) 在 setTimeout 执行完3秒后 do stuff 会输出到控制台。我们可以看出当指定的时间过去后我们所提供的异步函数会触发。现在来看个更有启发性的示例: doWork( () => { console.log('call me whe

  • 问题内容: 我们正在与节点合作,主要用于内部项目,并了解使用该技术的最佳方法。 并非来自特定的异步背景,学习曲线可能是一个挑战,但是我们已经习惯了框架和学习过程。 使我们两极分化的是,何时才是使用同步代码与异步代码的最佳时间。我们目前使用的规则是,如果任何东西与IO进行交互,那么它必须通过回调或事件发射器(即给定的)是异步的,但是可以将任何未使用IO的其他项构造为同步函数(此方法还将取决于函数本身

  • 这是我的代码: 如果我先调用readAllData(),然后再调用readData(),我会得到一个RangeError:

  • 我可能还不够清楚--情况是,我的现有代码不支持异步,我希望使用新的库,如System.net.http和只支持异步方法的AWS SDK。因此,我需要弥补这一差距,并能够拥有可以同步调用的代码,然后可以在其他地方调用异步方法。 我读了很多书,有很多次有人问这个问题,也有人回答这个问题。 从非异步方法调用异步方法