Search code examples
reactjsreact-hooksaveragereact-componentreact-functional-component

React: trying to get an average number from data fetched from db


I am trying to make a react component that returns the average number after fetching the data from the DB and converting it to json.

Total novice here. I am trying to make a react component that returns the average number after fetching the data from the DB and converting it to json. This is the json I am working with, which is fetched succesfully, but I am not sure how to do the calculation in react. Basically I am trying to get the average from all the numbers in the field "ivertinimai".enter image description here

This is the component:


import React, { useState, useEffect, useContext, useRef }  from 'react';

const AverageEvaluation = (props) => {
let [allEvaluations, setAllEvaluations] = useState()
let [evaluationsArray, setEvaluationsArray] = useState([])
let [sum, setSum] = useState(0)
let [average, setAverage] = useState(0)

useEffect(() => {
    fetch("http://127.0.0.1:8000/api/getevaluations/" + props.mealId, {method: 'GET', headers: { 'Content-Type': 'application/json' }})
    .then(response => response.json())
    .then(response=>{setAllEvaluations(allEvaluations=response); console.log(response)})
}, []);

let getAverage = () => {
    allEvaluations.map((meal, index)=>setEvaluationsArray([...evaluationsArray, meal.ivertinimai]))
    setSum(evaluationsArray.reduce((a,v) =>  a = a + v, 0 ))
    setAverage(average = sum/evaluationsArray.length)
}

return (
    <>
        {average}
    </>
);

};


The component doesn't seem to return any result after I import and add it in another component. I'm not sure if the problem is in the function to calculate the average or is it that I am trying to use react in the wrong way here. Would appreciate your help.


Solution

  • There are a few things I see that need a change:

    1. It should be const [ ..., ... ] = ... not let [..., ...]

    2. I would put an empty array into initial declaration of allEvaluations -> useState([]);

    3. setAllEvaluations(allEvaluations=response) and setAverage(average = sum/evaluationsArray.length) should be setAllEvaluations(response) and setAverage(sum/evaluationsArray.length)`

    Variables allEvaluations and average should not be changed, so writing avegare = ... is not only incorrect but also if written in set functions will not result in changing their values. That's because you are not putting there a new value but an assignment to a variable.

    Similarly in reduce((a,v) => a = a + v, 0 ), just write reduce((a,v) => a + v, 0 ) - no assignment needed.

    1. Your getAverage function.

    Firstly, instead of

    allEvaluations.map((meal, index)=>setEvaluationsArray([...evaluationsArray, meal.ivertinimai]));
    setSum(evaluationsArray.reduce((a,v) =>  a = a + v, 0 ));
    setAverage(average = sum/evaluationsArray.length);
    

    try something like this:

    const newEvaluationsArray = allEvaluations.map((meal) => meal.ivertinimai);
    setEvaluationsArray(newEvaluationsArray);
    const newSum = newEvaluationsArray.reduce((a,v) =>  a + v, 0 );
    setSum(newSum);
    setaverage(newSum/newEvaluationsArray.length);
    

    In the map function you wrote, you needlessly call setEvaluationsArray for each meal object and needlessly create new and new arrays ([...evaluationsArray, meal.ivertinimai]).

    Then in your setSum and setAverage, you are using the useState variable (evaluationsArray) which you have just modified. I would not recommend doing this, as when I did it sometimes there was still the old value, not the just updated one. (I'm not sure about the inner workings of useState and why/when is it happening, but I prefer to be cautious). The same applies for sum and setAverage.

    In my code, I first map allEvaluations and then assign them to a constant. This creates just one new array instead of multiple. This also makes it so that you can both set the new value in setEvaluationsArray and be sure in setSum that it is the new value you wanted. I did a similar thing with newSum.

    Secondly, you aren't calling your getAverage function anywhere. There are multiple options for you. Probably the easiest option is to put it in useEffect after setAllEvaluations(response); You will have to change getAverage function to accept arguments, for the same reason we made newEvaluationsArray and newSum above:

    const getAverage = (response) => {      
      const newEvaluationsArray = response.map((meal) => meal.ivertinimai);
      setEvaluationsArray(newEvaluationsArray);
      const newSum = newEvaluationsArray.reduce((a,v) =>  a + v, 0 );
      setSum(newSum);
      setaverage(newSum/newEvaluationsArray.length);  
    }
    

    At this point, you could just rewrite the body of getAverage into the useEffect and remove getAverage, if this is the only place where you will call it.

    Hope it helps, good luck!