Ali Qumail
Ali Qumail

Reputation: 43

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;

Upvotes: 3

Views: 4849

Answers (2)

Shubham Khatri
Shubham Khatri

Reputation: 281686

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

Upvotes: 6

Robin Zigmond
Robin Zigmond

Reputation: 18249

The problem is on this line (repeated multiple times):

setPoints(...newPoints)

it should be:

setPoints(newPoints)

newPoints is an array, and contains the new array you want assigned to the points state variable - so calling setPoints with that array as argument is exactly what you want.

Whereas when you use the "spread" operator, you are calling setPoints with several arguments, one for each element in the array. Since React's "state update" functions only accept a single argument, you're effectively setting the state to just the first element of the array. Since that's not itself an array, this means the next time this code is executed, it's trying to "spread" (use ...) on a plain number such as 4, hence the error message.

Upvotes: 3

Related Questions