0

I have a react weather project which is based on API I have a component called CHART but it is not updating the most important part of the project. Whenever I type the city name it is getting all the information's and updating the written part of the project. But it is not doing it with the chart.And it is always displaying the first values of first call but not updating. When I come in to the Chart.js file and make a space in the project and hit save it is displaying the correct chart based on API . I can smell there must be use case of useEffect Hook but how ?

Here is the Child Component

import React, {  useEffect, useState } from 'react';
import { Line } from 'react-chartjs-2';
import { Chart as ChartJS} from 'chart.js/auto';

const Chart = ({following}) => {
  const date = following.map(item => new Date(item.dt * 1000));
  const dateLocal = date.map(item => item.toLocaleString('default', { weekday: 'long' }));
  const temp = following.map(item => Math.floor(item.temp.day));



  const [userData, setUserData] = useState({
    labels: dateLocal,
    datasets: [
      { 
        data: temp,    
        label: 'Temperature',
        backgroundColor: [
          'rgba(0, 0, 0,.5)',
          'rgba(0, 0, 0,.5)',
          'rgba(0, 0, 0,.5)',
        ],
        color : ['rgba(0,0,0,1)'],
        fill:true,  
        pointRadius: 1,
        pointHitRadius:4,
        pointHoverRadius:8,
        pointBackgroundColor: 'mediumblue',
        borderWidth: 2,
        pointBorderColor: 'lightblue',
        pointBorderWidth: 2,
        pointHoverBorderWidth: 2,
        tooltips: {
          mode: 'index',
          intersect: true,
          windowPadding: 10,
        },
        scales: {
          xAxes: [{
            gridLines: {
              display: false,
            },
            ticks: {
              fontColor: 'green',
              zeroLineColor: 'blue',
              callback : 
               function (value) {
                  return value
                }
            }
          }],
          yAxes: [{
            gridLines: {
              display: false,
            },
            ticks: {
              fontColor: 'black',
              fontSize: 112,
              fontStyle: 'bold',
              color: 'red',
              callback :
                function (value) {
                  return value
                }
            }
          }]
        }
      }
    ]
  });

  return (
    <div className="line" style={{ width:'100%'}}>
    <Line  
      data={userData} 
     />
    </div>
  )
};

export default Chart;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Here is the Parent App.js

import React, { useEffect, useState } from 'react';
import Chart from './Chart';

const api = {
  key: `${process.env.REACT_APP_API_KEY}`,
  base: 'https://api.openweathermap.org/data/2.5/'
}

function App() {

  const [query, setQuery] = useState('');
  const [weather, setWeather] = useState({});
  const [location, setLocation] = useState({ lat: '', lon: '' });
  const [following, setFollowing] = useState([]);
  


  const search = async (e) => {
    if (e.key === 'Enter') {
      await fetch(`${api.base}weather?q=${query}&units=metric&appid=${api.key}&lang=tr`)
        .then(res => res.json())
        .then(result => {
          setWeather(result);
          setQuery('');
          setLocation(result.coord);
          console.log(result);
          searchFollowing();
        }
        )
    }
  }



  useEffect(() => {
    if (location.lat && location.lon) {
      searchFollowing();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [location])

  const searchFollowing = async () => {
    await fetch(`${api.base}onecall?lat=${location.lat}&lon=${location.lon}&units=metric&exclude=hourly,minutely&appid=${api.key}`)
      .then(res => res.json())
      .then(result2 => {
        const array = result2.daily.slice(1, 6);
        console.log(following);
        setFollowing(array);
        // following == array
      }
      )
  }



  const integer = (number) => {
    return Math.floor(Math.round(number));
  }
 

  const mapped = (following) => {
    following = [...following];
    return following.map((item, idx) => {
      const date = new Date(item.dt * 1000);
      const date1 = date.toLocaleString('default', { weekday: 'long' });
      const icon = item.weather[0].icon;
      const day = integer(item.temp.day);
      const night = integer(item.temp.night);
      return (
        <div key={idx} className="box">
          <h4>{date1}</h4>
          <img
            src={`http://openweathermap.org/img/wn/${icon}.png`}
            alt='weather'
            width={100}
            height={100}
          />
          <h4>Gündüz &nbsp; &nbsp; {day} °C</h4>
          <h4>Gece &nbsp; &nbsp; {night} °C</h4>
        </div>
      )
    })
  }

  const dateBuild = (d) => {
    let months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
    let days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']

    let day = days[d.getDay()]
    let date = d.getDate()
    let month = months[d.getMonth()]
    let year = d.getFullYear()

    return `${day} ${date} ${month} ${year}`
  }


  return (
    <div className={(typeof weather.main !== 'undefined') ?
      ((weather.main.temp > 25) ? 'App hot' :
        ((weather.main.temp < 25 && weather.main.temp > 5) ?
          'App warm' : 'App')) : 'App'}>
      <main>
        <div className="search-box">
          <input
            type="text"
            className="search-bar"
            placeholder="Search for a location..."
            onChange={e => setQuery(e.target.value)}
            onKeyPress={search}
            value={query}
          />
        </div>
        {(typeof weather.main != "undefined") ? (
          <div className="content">
            <div className="location-box">
              <div className="location">
                {weather.name}, {weather.sys.country}
              </div>
              <div className="date"> {dateBuild(new Date())}
              </div>
            </div>
            <div className="weather-box">
              <div className="temp">
                {Math.round(weather.main.temp)}°C
                <img
                  src={`http://openweathermap.org/img/wn/${weather.weather[0].icon.slice(0, 2)}d.png`}
                  alt='weather'
                  width={150}
                  height={150}
                />
              </div>
              <div className="weather">
                <p>
                  <span>Hissedilen</span>
                  {Math.floor(weather.main.feels_like)} °C
                </p>
                <p>
                  <span>Şu an</span>
                  {weather.weather[0].description}
                </p>
                <p>
                  <span>Basınç</span>
                  {weather.main.pressure} mb
                </p>
                <p>
                  <span>Rüzgar </span>
                  {Math.floor(weather.wind.speed)} km/h
                </p>
                <p>
                  <span>En fazla</span>
                  {Math.floor(weather.main.temp_max)} °C
                </p>
                <p>
                  <span>En az</span>
                  {Math.floor(weather.main.temp_min)} °C
                </p>
              </div>
            </div>
            <Chart following={following} />
            <div className="followingdays"
            >
              {mapped(following)}
            </div>
          </div>) : ('')}
      </main>
    </div>
  );
}

export default App;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
barisdevjs
  • 59
  • 3
  • 10

2 Answers2

1

UPDATE1 ==> I did it with the useEffect hook can't able to do it with a built-in method update()

import React, {  useEffect, useState } from 'react';
import { Line } from 'react-chartjs-2';
import { Chart as ChartJS} from 'chart.js/auto';



const Chart = ({following}) => {
  const date = following.map(item => new Date(item.dt * 1000));
  const dateLocal = date.map(item => item.toLocaleString('default', { weekday: 'long' }));
  const temp = following.map(item => Math.floor(item.temp.day));

  // if userData changes then the chart will re-render
    useEffect(() => {
      setUserData({ 
        labels: dateLocal,
        datasets: [
          { 
            data: temp,       
            label: 'Temperature', 
            backgroundColor: [
              'rgba(0, 0, 0,.5)',
              'rgba(0, 0, 0,.5)',
              'rgba(0, 0, 0,.5)', 
            ],
            color : ['rgba(0,0,0,1)'],
            fill:true,  
            pointRadius: 1,
            pointHitRadius:4,
            pointHoverRadius:8,  
            pointBackgroundColor: 'mediumblue',
            borderWidth: 2,
            pointBorderColor: 'lightblue',
            pointBorderWidth: 2,
            pointHoverBorderWidth: 2, 
            tooltips: {
              mode: 'index',
              intersect: true,
              windowPadding: 10,
            },
            scales: {
              xAxes: [{
                gridLines: {
                  display: false,
                },
                ticks: {
                  fontColor: 'green'
                }
              }],
              yAxes: [{
                gridLines: {
                  display: false,
                }, 
                ticks: { 
                  fontColor: 'green' 
                }
              }] 
            }
          } 
        ]   
      });
    },[following]);


 
  const [userData, setUserData] = useState({ 
    labels: dateLocal,
    datasets: [ 
      { 
        data: temp,       
        label: 'Temperature', 
        backgroundColor: [ 
          'rgba(0, 0, 0,.5)',
          'rgba(0, 0, 0,.5)',
          'rgba(0, 0, 0,.5)',   
        ],
        color : ['rgba(0,0,0,1)'], 
        fill:true,  
        pointRadius: 1,
        pointHitRadius:4,
        pointHoverRadius:8,  
        pointBackgroundColor: 'mediumblue',
        borderWidth: 2,
        pointBorderColor: 'lightblue', 
        pointBorderWidth: 2,
        pointHoverBorderWidth: 2, 
        tooltips: {
          mode: 'index',
          intersect: true,
          windowPadding: 10,
        },
        scales: {
          xAxes: [{
            gridLines: {
              display: false,
            },
            ticks: {
              fontColor: 'green'
            }
          }],
          yAxes: [{
            gridLines: { 
              display: false,
            }, 
            ticks: { 
              fontColor: 'green' 
            }
          }] 
        } 
      } 
    ]     
  });

  return (
    <div className="line" style={{ width:'100%'}}>
    <Line className="actual-chart"
      data={userData}
      redraw={true}
     />    
    </div>
  ) 
}; 
  
export default Chart;
   
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
barisdevjs
  • 59
  • 3
  • 10
0

Chart.js has update method. Try save your chart in variable and then call foo.update(), when necessary

Rodion
  • 27
  • 5
  • I can't able to do it with the update() method. But I did it with the useEffect() hook and draw the entire app with repeating logic @Rodion – barisdevjs Feb 06 '22 at 10:20