Search code examples
javascriptreactjsuse-effectuse-state

React js : Invalid attempt to spread non-iterable instance. In order to be iterable, non-array objects must have a [Symbol.iterator]() method


I'm trying to make gpa calculator that need letter grade and credit hours to calculate gpa. Letter grade help to find points for eg A+ means 4.00 or D means 1.00.

The problem is that when I change my grade value more than once I get this error and my error line is always in handlePoints() function The exact lines are

const newPoints = [...points];
newPoints[i] = 4;
setPoints(...newPoints);

Below is all code I have

import React, { useEffect, useState } from "react";
import SgpaComponent from "./SgpaComponent";

function Sgpa() {
  const [subjects, setSubjects] = useState(["Subject Name"]);
  const [grades, setGrades] = useState(["A+"]);
  const [points, setPoints] = useState([4]);
  const [credits, setCredits] = useState([3]);
  const [sgpa, setSgpa] = useState();
  function handleAdd() {
    setSubjects((prev) => [...prev, "Subject Name"]);
    setGrades((prev) => [...prev, "A+"]);
    setPoints((prev) => [...prev, 4]);
    setCredits((prev) => [...prev, 3]);
  }
  useEffect(() => {
    handlePoints();
    calcSgpa();
  });

  function calcSgpa() {
    let totalCredits = 0;
    let totalPoints = 0;
    for (let i = 0; i < credits.length; i++) {
      totalCredits += credits[i];
    }
    for (let i = 0; i < points.length; i++) {
      totalPoints += points[i];
    }
    setSgpa((totalCredits * totalPoints) / totalCredits);
  }

  function handleGrade(event, i) {
    let newGrades = [...grades];
    newGrades[i] = event.target.value;
    setGrades(...newGrades);
    
  }
  function handleCredits(event, i) {
    let newCredits = [...credits];
    newCredits[i] = event.target.value;
    setCredits(...newCredits);
  }

  function handlePoints() {
    for (let i = 0 ; i<grades.length ; i++){
    if (grades[i] === "A+" || grades[i] === "A") {
      const newPoints = [...points];
      newPoints[i] = 4;
      setPoints(...newPoints);
    }
    if (grades[i] === "A-") {
      let newPoints = [...points];
      newPoints[i] = 3.67;
      setPoints(...newPoints);
    }
    if (grades[i] === "B+") {
      let newPoints = [...points];
      newPoints[i] = 3.33;
      setPoints(...newPoints);
    }

    if (grades[i] === "B") {
      let newPoints = [...points];
      newPoints[i] = 3.0;
      setPoints(...newPoints);
    }
    if (grades[i] === "B-") {
      let newPoints = [...points];
      newPoints[i] = 2.67;
      setPoints(...newPoints);
    }
    if (grades[i] === "C+") {
      let newPoints = [...points];
      newPoints[i] = 2.33;
      setPoints(...newPoints);
    }
    if (grades[i] === "C") {
      let newPoints = [...points];
      newPoints[i] = 2.0;
      setPoints(...newPoints);
    }
    if (grades[i] === "C-") {
      let newPoints = [...points];
      newPoints[i] = 1.67;
      setPoints(...newPoints);
    }
    if (grades[i] === "D+") {
      let newPoints = [...points];
      newPoints[i] = 1.37;
      setPoints(...newPoints);
    }
    if (grades[i] === "D") {
      let newPoints = [...points];
      newPoints[i] = 1.0;
      setPoints(...newPoints);
    }
    if (grades[i] === "F") {
      let newPoints = [...points];
      newPoints[i] = 0;
      setPoints(...newPoints);
    }
    }
  }

  return (
    <>
      <h3>Sgpa : {sgpa}</h3>
      {subjects.map((subject, i) => {
        return (
          <SgpaComponent
            subject={subject}
            grade={grades[i]}
            credit={credits[i]}
            point={points[i]}
            index={i}
            handleGrade={handleGrade}
            handleCredits={handleCredits}
          />
        );
      })}
      <button onClick={handleAdd}>+</button>
    </>
  );
}

export default Sgpa;

Solution

  • When you set your state like setPoints(...newPoints); you essentially cause the state to change from holding an array to an element. So the next time you call it you don'thave an array and hence you can't spread it

    The correct way to set state is to use setPoints(newPoints). The same thing needs to be done for setGrades(...newGrades); and setCredits(...newCredits); which would change to setGrades(newGrades); and setCredits(newCredits);

    Also all instances of setPoints(...newPoints); should be updated to setPoints(newPoints) in your code