1

In my ReactJS application i need to display data in Graphs and i'm using chart-js-2 library. In the dashboard i've implemented a Doughnut chart and Bar chart. The graphs are initially populated using useEffect, but the data contained can be filtered by year and by type of tax. Specifically, on the donut chart, when filter by reference year, even if the data is retrieved correctly, the chart does not update. If I then filter again, the graph updates, but with the data of the previous filter, falling behind by one event.

For example: Initially I show data for all years, filter for a specific year (ex: 2022) and the graph does not change. Then filter again by year (Ex: 2021), the filter indicates 2021 but the graph is updated with the data relating to the filter year = 2022.

Do you have any suggestions or advice? I'm fairly new to React, am I doing something wrong?

-my Code:

import React, { useEffect, useState } from "react";
import Grid from "@material-ui/core/Grid";
import DashCard from "./DashboardCard" // eliminabile
import HomeDoughChart from '../charts_js/HomeDoughChart'
import tableIcons from '../templates/TableIcons';
import HomeBarChart from "../charts_js/HomeBarChart";
import MaterialTable from "material-table";
import {
   getDashboard_Data,
   getDashboard_Data_byYear,
   getDashboard_TributeFiler,
   getDashboard_YearFiler,
   getDashboard_Data_byYear_andTribute,
   getDashboard_Contribuenti_byYear_andTribute,
   getTableData
} from "../../repo/dashboardRepo";
import { Card, CardContent, CardHeader } from "@material-ui/core";
import { SettingsBackupRestoreOutlined } from "@material-ui/icons";

const DashboardContent = () => {
   const [chart, setChart] = useState([])
   const [barchart, setBarChart] = useState([])
   const [barchart1, setBarChart1] = useState([])
   const [barchart2, setBarChart2] = useState([])
   const [barchart3, setBarChart3] = useState([])
   const [trib, setTributi] = useState([])
   const [year, setAnni] = useState([])
   const [dashboardfilter, setDashboardFilter] = useState({ year: "", tributo: "" });
   const [barTribute, setBarTribute] = useState([]);
   const [graphData, setGraphData] = useState([]);

   function handleYearChange(evt) {
      setDashboardFilter({ year: evt.target.value, tributo: dashboardfilter.tributo });
      const yf = async () => {
         const res = (dashboardfilter.year !== "") ? await getDashboard_Data_byYear_andTribute(dashboardfilter.year, dashboardfilter.tributo) : await getDashboard_Data();
         //const res1 = (dashboardfilter.year !== "") ? await getDashboard_Contribuenti_byYear_andTribute(dashboardfilter.year, dashboardfilter.tributo) : await getDashboard_Contribuenti_byYear_andTribute();
         setChart(res.data.data);
      }
      yf();
   }

   function handleTributeChange(evt) {
      setDashboardFilter({ year: dashboardfilter.year, tributo: evt.target.value });
      const yf = async () => {
         const res = (dashboardfilter.tributo !== "") ? await getDashboard_Data_byYear_andTribute(dashboardfilter.year, dashboardfilter.tributo) : await getDashboard_Data();
         //const res1 = (dashboardfilter.year !== "") ? await getDashboard_Contribuenti_byYear_andTribute(dashboardfilter.year, dashboardfilter.tributo) : await getDashboard_Contribuenti_byYear_andTribute();
         setChart(res.data.data);
      }
      yf();
   }

   function handleBarTributeChange(evt) {

      setBarTribute({ b_trib: evt.target.value });
      const tf = async () => {
         const res1 = (barTribute.b_trib === "Tutti") ? await getDashboard_Data_byYear(2022) : await getDashboard_Data_byYear_andTribute(2022, barTribute.b_trib);
         const res2 = (barTribute.b_trib === "Tutti") ? await getDashboard_Data_byYear(2021) : await getDashboard_Data_byYear_andTribute(2021, barTribute.b_trib);
         const res3 = (barTribute.b_trib === "Tutti") ? await getDashboard_Data_byYear(2020) : await getDashboard_Data_byYear_andTribute(2020, barTribute.b_trib);
         const res4 = (barTribute.b_trib === "Tutti") ? await getDashboard_Data_byYear(2019) : await getDashboard_Data_byYear_andTribute(2019, barTribute.b_trib);
         setBarChart(res1.data.data);
         setBarChart1(res2.data.data);
         setBarChart2(res3.data.data);
         setBarChart3(res4.data.data);
      }
      tf();
   }

   useEffect(() => {
      const a = async () => {
         if (dashboardfilter.year === "" && dashboardfilter.tributo === "") {
            const res = await getDashboard_Data();
            //const res1 = await getDashboard_Contribuenti_byYear_andTribute();
            setChart(res.data.data);
         }
      }
      a()
   }, [])

   useEffect(() => {
      var today = new Date();
      const year = today.getFullYear()
      const b = async () => {
         const res1 = await getDashboard_Data_byYear(year);
         const res2 = await getDashboard_Data_byYear(year-1);
         const res3 = await getDashboard_Data_byYear(year-2);
         const res4 = await getDashboard_Data_byYear(year-3);
         setBarChart(res1.data.data);
         setBarChart1(res2.data.data);
         setBarChart2(res3.data.data);
         setBarChart3(res4.data.data);
      }
      b()
   }, [])

   useEffect(() => {
      const c = async () => {
         const res2 = await getDashboard_TributeFiler();
         const res3 = await getDashboard_YearFiler();
         setTributi(res2.data.data);
         setAnni(res3.data.data);
      }
      c();
   }, [])

   useEffect(() => {
      const t = async () => {
         const res = await getTableData();
         setGraphData(res.data.data);
      }
      t();
    }, []);

   return (
      <div>
         <div style={{ display: "flex", flex: "1", flexDirection: "row", alignItems: "stretch", justifyContent: 'space-evenly' }}>
            <Card style={{ marginLeft: 30, responsive: true, marginTop: 30, marginRight: 30, flex: 0.85 }}>
               <CardHeader title={"Consuntivo totale dei pagamenti"} style={{ textAlign: "left" }} />
               <CardContent>
                  <Grid container direction="row" style={{ marginTop: '10px', marginLeft: '20px', marginBottom: '60px' }} >
                     <div>
                        <form justifyContent="left">
                           <label>Filtra per anno di riferimento </label>
                           <select name="year" value={dashboardfilter.year} onChange={handleYearChange}>
                              <option value="">Tutti gli anni</option>
                              {year.map(y => {
                                 return (
                                    <option key={y.anno} value={y.anno}>
                                       {y.anno}
                                    </option>
                                 )
                              })}
                           </select>
                           <br />
                           <label>Filtra per tributo </label>
                           <select name="tributo" value={dashboardfilter.tributo} onChange={handleTributeChange}>
                              <option value="">Tutti i tributi</option>
                              {trib.map(tributi => {
                                 return (
                                    <option key={tributi.descrizioneServizio} value={tributi.descrizioneServizio}>
                                       {tributi.descrizioneServizio}
                                    </option>
                                 )
                              })}
                           </select>
                        </form>
                        <br />
                        <h3>Totale da riscuotere: {Intl.NumberFormat('it-IT', { style: 'currency', currency: 'EUR' }).format(chart.totaleDaPagare)} </h3>
                        <HomeDoughChart chart={chart} />
                     </div>
                  </Grid>
               </CardContent>
            </Card>
            <Card style={{ marginLeft: 30, responsive: true, marginTop: 30, marginRight: 30, flex: 1 }}>
               <CardHeader title={"Consuntivo dei pagamenti per anno"} style={{ textAlign: "left" }} />
               <CardContent >
                  {/* qui dobbiamo inserire i filtri, il titolo del grafico e il valore del filtro per tributo del grafico a istogramma */}
                  <Grid container direction="row" justify="left" alignItems="center" style={{ marginTop: '10px', marginLeft: '20px', marginBottom: '60px' }}>
                     <div>
                        <form>
                           <label>Filtra per tributo </label>
                           <select name="tributoBar" value={barTribute.b_trib} onChange={handleBarTributeChange}>
                              <option value="Tutti">Tutti i tributi</option>
                              {trib.map(tributi => {
                                 return (
                                    <option key={tributi.descrizioneServizio} value={tributi.descrizioneServizio}>
                                       {tributi.descrizioneServizio}
                                    </option>
                                 )
                              })}
                           </select>
                        </form>
                        <br />
                        <h3>Tributo di riferimento: {barTribute.b_trib}</h3>
                     </div>
                     <HomeBarChart barchart={barchart} barchart1={barchart1} barchart2={barchart2} barchart3={barchart3} />
                  </Grid>
               </CardContent>
            </Card>
         </div>
         <div style={{ display: "flex", flex: "1", flexDirection: "row", alignItems: "center", responsive: true }}>
            <Grid container spacing={1} style={{ marginLeft: 30, marginTop: 30, marginRight: 30, flex: 1 }}>
               <MaterialTable
                  title="Consuntivo posizioni"
                  options={{
                     sorting: true,
                     actionsColumnIndex: -1,
                     pageSize: 5,
                     toolbar: true,
                     paging: false,
                     responsive: true,
                     exportButton: true,
                     exportAllData: true,
                     exportFileName: "ConsuntivoPosizioni",
                     filtering: false,
                     grouping: true,
                  }}
                  icons={tableIcons}
                  columns={[
                     { title: 'Tributo', field: 'tributo' },
                     { title: 'Anno Riferimento', field: 'docAnnoRif', defaultGroupSort: "desc"},
                     { title: '#Contribuenti', field: 'totcontribuenti' },
                     { title: '#Documenti', field: 'atti', type: 'numeric' },
                     { title: 'Importo pagamento', field: 'totaleDovuto',type: "currency", currencySetting:{ currencyCode:'EUR', minimumFractionDigits:2, maximumFractionDigits:2} },
                     { title: 'Importo pagato', field: 'totaleVersato',type: "currency", currencySetting:{ currencyCode:'EUR', minimumFractionDigits:2, maximumFractionDigits:2} },
                     { title: 'Importo restante', field: 'restante', type: "currency", render: rowData => (Intl.NumberFormat('it-IT', { style: 'currency', currency: 'EUR' }).format(rowData.totaleDovuto-rowData.totaleVersato)) },
                  ]}
                  data={(dashboardfilter.year === "" && dashboardfilter.tributo === "") ? graphData : graphData.filter(element => element.docAnnoRif === dashboardfilter.year)}
               />
            </Grid>
         </div>
      </div>
   )
}
export default DashboardContent;
64Bit1990
  • 302
  • 2
  • 16
  • Why are using 4 useEffect. Defining 1 is enough right?? – Aravind Aug 01 '22 at 07:10
  • i've used 4 useEffect because i've 4 component to populate on loading, it is possible with one? – 64Bit1990 Aug 01 '22 at 07:14
  • Since you didn't provide any props in the second argument of the `useEffect`'s, they will all be executed only once, when the component is mounted – rvanlaarhoven Aug 01 '22 at 07:17
  • 1
    Yes if the listeners are same you can use one in your case ```[]``` is the listner for 4 use effect so you can use one. For more info refer this: https://stackoverflow.com/questions/54002792/in-general-is-it-better-to-use-one-or-many-useeffect-hooks-in-a-single-component – Aravind Aug 01 '22 at 07:17
  • @Aravind thanks...if i understand correctly, with one useEffect can i populate all chart and table on first mount, and use other useEffect only for re-render the component – 64Bit1990 Aug 01 '22 at 07:22
  • yes you can do that – Aravind Aug 01 '22 at 07:23
  • Can you provide a preview link? like CodeSandbox – Black Plum Aug 05 '22 at 10:44

1 Answers1

1

I think the problem is that for example within the handleYearChange, you don't actually use the new value to query the results. You set the dashboardFilter with the new values and then expect them to be accessible right away after that. Instead, you should probably use the evt.target.value in the async function after that:

function handleYearChange(evt) {
      setDashboardFilter({ year: evt.target.value, tributo: dashboardfilter.tributo });
      const yf = async () => {
         const res = (evt.target.value !== "") ? await getDashboard_Data_byYear_andTribute(evt.target.value, dashboardfilter.tributo) : await getDashboard_Data();
         setChart(res.data.data);
      }
      yf();
   }
rvanlaarhoven
  • 591
  • 6
  • 16
  • Thanks, I had already tried this but without success, I found the solution in the link posted in the comments to my question – 64Bit1990 Aug 01 '22 at 07:52