Having trouble updating a React component using a Hook with a timer

Viewed 47

I have a graph which I'm trying to update every second. In the parent component I have a timer, each second I generate a random value and parse it as a prop to the child component.

import './main.css'
import { Graph } from '../../components/graph_recharts'


export const Main = () => {
    const [timer, setTimer] = useState("")
    const [data, setData] = useState(0)
    useEffect(() => {
        let value = 1000
        const create_value = () =>{
            if (positive_trend() || value - generate_random_value() < 0){
                return value+= generate_random_value()
            }
            return value-=generate_random_value() 
        }

        const timer = (mins, secs) => {
                        \\timer code omitted to save space
                        setData(create_value())
                    }, 1000);    

                setTimeout(() => { clearInterval(timerId); setTimer('stop'); }, totalTimer);
            }
            else {
                console.log('error')
            }
        }
        timer(5, 1)

    }, [])


    return (
        <div className='container'>
            <div className='header'>
                <h1>{timer}</h1>
            </div>
            <div>
                <Graph data={data} />
            </div>
            <div className="footer">
                footer
            </div>
        </div>

    );
}

In the child component I then try to set the data and I attempted to use the useEffect hook to help me. However I get an error telling me I'm trying to update an unmounted component. I'm not sure how to solve this issue. my child component is below.

import React, { useState,useEffect } from 'react'
import {LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend} from 'recharts';


export const Graph = (props)=>{


const [data,setData] = useState([{name:'0', price: props.data}])

  useEffect(() => {
    setData(...data,{name:'0', price: props.data })
    console.log(props.data)
  }, [props.data,data])
    


    return (
        <LineChart 
            width={window.innerWidth*0.9} 
            height={window.innerHeight*0.4} 
            data={data}
            margin={{top: 5, right: 30, left: 20, bottom: 5}}
        >
         <XAxis dataKey="name"/>
         <YAxis/>
         <CartesianGrid strokeDasharray="3 3"/>
         <Tooltip/>
         <Legend />
         <Line type="monotone" dataKey="uv" stroke="#8884d8" activeDot={{r: 8}}/>
        </LineChart>
    );

}
2 Answers

Wrap the data parsed in setData in square braces like so:

setData([...data,{name:'0', price: props.data }])

remove data from the useEffect dependencies

You want to set the data only when props.data changes, the way your effect currently works has local state data be a dependency of the effect so you'll get an infinite loop. When props.data changes it'll set data but that changes data so the effect is run again.

Do the following instead:

useEffect(() => {
  setData((data) => [//callback for state setter
    ...data,
    { name: '0', price: props.data },
  ]);
}, [props.data]);//local state data no longer a dependency
Related