How to calculate percentage on state change in react hooks

i am having the below state

const [data,setData] = useState({maths:null,physics:null,chem:null,percentage:null})

and i was updating the state as

setData({...data,[e.target.name]:e.target.value})

and my fuction for calculating the percentage is

function percentage(data.maths,data.physics,data.chem){ 
     const per = ((data.maths+data.physics+data.chem)/100)*100
return per
}

my doubt is when to call this function(like useEffect or like normal) and store it in data.percentage
please help me

37 thoughts on “How to calculate percentage on state change in react hooks”

  1. Try doing the function in useState as shown below and as mentioned pass the state as the array deps, so whenever any of the property in the state changes it will recalculate, which is your required output.

    or as Germa suggested you can skip the function itself and do it something like this

    const [avg, setAvg] = useState(0)
    
    useEffect(() => {
        const {
            maths,
            physics,
            chem
        } = data
        const per = ((data.maths + data.physics + data.chem) / 100) * 100
        setState(per)
    }, [data])
    

    I have added the codesandbox based on the discussion, having it in diff state will be much more better way, or else when you pass the array deps it will keep on changing.

    Reply
  2. First of all, percentage should be divided by 300 I guess.
    The percentage was a part of the same data as your input field so it was causing infinite loop whenever we were setting percentage in useEffect. So, it’s better to separate the percentage from the input data of subjects.

    Working Demo

    import { useEffect, useState } from "react";
    
    const App = () => {
      const [data, setData] = useState({ maths: 0, physics: 0, chem: 0 });
      const [percentage, setPercentage] = useState(0);
    
      const fields = [
        { name: "maths", value: data.maths },
        { name: "physics", value: data.physics },
        { name: "chem", value: data.chem }
      ];
    
      useEffect(() => {
        // Not sure but percentage should be divided 300 I guess
        const per = ((data.maths + data.physics + data.chem) / 300) * 100;
        setPercentage(per);
      }, [data]);
    
      const handleChange = (e) => {
        console.log(e.target.value);
        setData({
          ...data,
          [e.target.name]: parseInt(e.target.value)
        });
      };
    
      return (
        <div>
          <h1>Percentage: {percentage}</h1>
          {fields.map((field, idx) => (
            <input
              key={idx}
              placeholder={field.name}
              name={field.name}
              value={field.value}
              onChange={handleChange}
            />
          ))}
        </div>
      );
    };
    
    export default App;
    
    Reply
  3. Like so:

    useEffect(() => {
        const { maths, physics, chem } = data;
        const percentage = ((maths + physics + chem) / 100) * 100;
        setData({ maths, physics, chem, percentage });
      }, [data]);
    

    the useEffect will run every time data changes, and data.percentage will be recalculated.

    Reply

Leave a Comment