【问题标题】:React useState hook is appending values instead of overwriting [duplicate]React useState 钩子正在附加值而不是覆盖[重复]
【发布时间】:2021-07-04 13:50:10
【问题描述】:

所以我想每 5 秒更新一次实时价格,因此我将递归 setTimeout 放入函数中。不幸的是,现在价格馈送是这样工作的:

  1. 一开始控制台输出为:未定义和当前价格
  2. 价格更改后输出为:未定义、旧价格、新当前价格
  3. 下一次价格更改后:未定义、最旧价格、旧价格、新当前价格

为什么它会附加值而不是像往常一样覆盖它们?

import React, { useState, useEffect } from 'react';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import Livebsv from './livebsv.js';

const CoinGecko = require('coingecko-api');
const CoinGeckoClient = new CoinGecko();


export default function Calculatorbuy() {
    const [value, setValue] = useState(0);
    
    const [price, setPrice] = useState();

    useEffect(() => {
        const fetchPrice = async () => {
            const result = await CoinGeckoClient.simple.price({
                ids: "bitcoin-cash-sv",
                vs_currencies: "pln",
            });
            setPrice(parseFloat(result.data.['bitcoin-cash-sv'].pln));
            console.log(price);
            setTimeout(fetchPrice, 5000)
        }; 
        fetchPrice(); 
    })

【问题讨论】:

  • console.log(price); 将输出旧价格。
  • 它是如何追加的?在我看来,它每次都有不同的价值
  • 还可以尝试将依赖数组添加到您的 useEffect 挂钩 [价格]
  • 您正在记录当前状态,请参阅stackoverflow.com/questions/54069253/…。此外,您的 useEffect 缺少依赖数组,因此每次组件呈现时您都会开始新的超时“链”。我猜你的意思是只在组件挂载时启动一个,所以使用一个空的依赖数组([])只运行一次效果。确保还捕获超时 id,以便您可以在返回的效果清理函数中取消任何正在运行的计时器。我也没有看到任何附加,你的状态是一个数字。
  • 完全同意@drew-reese 但是,正如@dario-k 所说,你为什么说要附加值? console.log 只显示一个正在使用不同值更新的数字

标签: javascript reactjs next.js bitcoin


【解决方案1】:

问题

  1. 您正在控制台记录您的状态,它在入队后立即记录,这只会记录当前渲染周期的状态,而不是下一个渲染周期入队的状态。
  2. 您的useEffect 挂钩在每次组件呈现时都会运行,因此您会启动多个超时。我怀疑您看到的“附加”是运行相同回调的重复超时的结果。

解决方案

  1. 组件挂载时使用空的依赖数组运行一次​​效果。
  2. 使用时间间隔运行提取。
  3. 返回清理函数以清除所有运行间隔。
  4. 控制台以自己的依赖关系记录更新的状态。

代码:

useEffect(() => {
  const fetchPrice = async () => {
    const result = await CoinGeckoClient.simple.price({
      ids: "bitcoin-cash-sv",
      vs_currencies: "pln",
    });
    setPrice(parseFloat(result.data.['bitcoin-cash-sv'].pln));
  };

  fetchPrice(); // <-- first initial fetch

  const timerId = setInterval(fetchPrice, 5000); // <-- start interval

  return () => clearInterval(timerId); // <-- return cleanup
}, []); // <-- run on mount

useEffect(() => {
  console.log(price); // <-- log updated state
}, [price]); // <-- run on price update

【讨论】:

    【解决方案2】:

    有很多问题。

    首先,在没有依赖关系的情况下调用useEffect 意味着它将在每次重新渲染后运行。即fetchPrice 本身会递归运行以获取和更新状态,每次更新都会导致组件重新渲染,从而触发useEffect 并因此触发setTimeout 堆栈。

    查看:https://reactjs.org/docs/hooks-effect.html#example-using-hooks

    改成

    useEffect(
      () => {
        // your code here
      },
      [],
    );
    

    那么它将只在挂载时运行。

    第二,如cmets中所说,setState是异步的,设置它然后立即记录它会显示旧的结果。

    【讨论】:

    • 这可能只是我迂腐,但setState 是 100% 同步的,它不返回 Promise 并且无法等待。它将更新加入队列,React 框架异步处理这些加入队列的更新。
    • 根据反应文档,对setState的调用是异步的(reactjs.org/docs/…
    • 一个不返回promise的函数并不一定意味着它是同步的,它只是没有提供在操作完成时通知调用者的方法。 setTimeout 也没有返回承诺,但它肯定是异步执行任务。我猜setState 在后台使用requestAnimationFrame,该值将在下一帧更新。
    • 不是很想进入辩论,但未声明 async 的函数是简单且绝对简单的旧同步函数。 React 状态更新函数 setTimeout 都是同步函数,它们将回调加入队列以异步处理。我想你可能误解了我的评论。 setState 是同步函数,React 状态更新是异步的。 “异步”!== async.
    猜你喜欢
    • 2021-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-04
    • 1970-01-01
    • 2021-01-17
    • 1970-01-01
    • 2020-07-01
    相关资源
    最近更新 更多