Search code examples
javascriptreactjsnext.jsbitcoin

React useState hook is appending values instead of overwriting


So i want to get live price update every 5 seconds therefore i put recursive setTimeout into the function. Unfortunately right now the price feed is working this way:

  1. At the start console outputs are: undefined and current price
  2. After price change outputs are: undefined, old price, new current price
  3. After next price change: undefined, oldest price, older price, new current price

why does it appending the values instead of overwriting them as usual?

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(); 
    })

Text


Solution

  • Issue

    1. You are console logging your state right after it was enqueued, which will only ever log the state from the current render cycle, not what was enqueued for the next render cycle.
    2. Your useEffect hook runs every time the component renders, so you are starting multiple timeouts. I suspect the "appending" you are seeing is the result of duplicate timeouts running the same callback.

    Solution

    1. Use an empty dependency array to run the effect once when the component mounts.
    2. Use an interval to run the fetching.
    3. Return a cleanup function to clear any running intervals.
    4. Console log the updated state in its own effect with dependency.

    Code:

    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