Home > OS >  Uncaught TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)) in my Reac
Uncaught TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)) in my Reac

Time:02-12

I am new to context API and trying to get my code to work. I am getting error:

Uncaught TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator))

Would some of you good people help with this?

The initialUserState logs an object the looks like this: {name: nanny}

My context file is:

import { createContext, useState } from "react";

const initialUserState = JSON.parse(localStorage.getItem("user"));
console.log(initialUserState);
const initialFormValues = {
  video: "Video.mp4",
  hourly: "",
  ageRange: [],
  car: "",
  languages: [],
  homework: "",
  license: "",
  preference: "",
  vaccination: "",
  radius: "",
  booked: "",
  fileUpload: "file.jpg",
};

export const Global = createContext();

export default function GlobalContext({ children }) {
  const [state, setState] = useState(initialUserState);
  const [values, setValues] = useState(initialFormValues); //setting form state
  const [radioSelected, setRadioSelected] = useState();

  const changeState = (newState = {}) => {
    const tempState = { ...state, ...newState };
    setState(tempState);
    localStorage.setItem("user", JSON.stringify(tempState));
  };

  return (
    <Global.Provider
      value={[
        { ...state },
        changeState,
        values,
        setValues,
        radioSelected,
        setRadioSelected,
      ]}
    >
      {children}
    </Global.Provider>
  );
}

then where I am getting state from, the initialFormValues does not log in the console:

const HourlyRate = () => {

  //context API

  const [values, setValues] = useContext(GlobalContext); **//this is where the error is being flagged from**

  //handle form value changes
  const handleChange = (e) => {
    e.preventDefault();

    const { name, value } = e.target;
    setValues({ ...values, [name]: value });
  };

  return (
    <div className="sub-settings-div">
      <Link className="back" to="/video-upload">
        <span>
          <ArrowBackIcon />
        </span>
        Back
      </Link>
      <h3 className="sub-settings-header">{t("common.title")}</h3>
      <h2 className="sub-settings-sub-header">{t("hourly.subTitle")}</h2>
      <p className="range">(10.45 &euro; - 20 &euro;) </p>
      <form className="input">
        <TextField
          className="input-field"
          required
          id="outlined-required"
          label="Hourly Rate"
          name="hourly"
          value={values.hourly}
          onChange={handleChange}
        />
      </form>
      <div className="nav-btns">
        <Link to="/video-upload" className="prev-link">
          <button className="prev">{t("buttons.back")}</button>
        </Link>
        <Link to="/age-range" className="next-link">
          <button className="next">
            {t("buttons.next")}
            <span>
              <ArrowForwardIcon />
            </span>
          </button>
        </Link>
      </div>
    </div>
  );
};

export default HourlyRate;

CodePudding user response:

Couple issues I see:

  1. You are passing the context provider component GlobalContext to the useContext hook, const [values, setValues] = useContext(GlobalContext);
  2. The context value is an array, so when using array destructuring the order of the array elements matters.

Use the correct variable for the context. You may want to give the context the GlobalContext name, and rename the provider something like GlobalProvider. I suggest also using an object for the context value to eliminate the array element order destructuring issue.

export const GlobalContext = createContext();

export default function GlobalProvider({ children }) {
  const [state, setState] = useState(initialUserState);
  const [values, setValues] = useState(initialFormValues); //setting form state
  const [radioSelected, setRadioSelected] = useState();

  const changeState = (newState = {}) => {
    const tempState = { ...state, ...newState };
    setState(tempState);
    localStorage.setItem("user", JSON.stringify(tempState));
  };

  return (
    <Global.Provider
      value={{
        state,
        changeState,
        values,
        setValues,
        radioSelected,
        setRadioSelected,
      }}
    >
      {children}
    </Global.Provider>
  );
}

Then assuming your main App component is correctly wrapped in the GlobalProvider, use object destructuring from the hook.

const { values, setValues } = useContext(GlobalContext);

CodePudding user response:

Your value is:

[
    { ...state },
    changeState,
    values,
    setValues,
    radioSelected,
    setRadioSelected,
  ]

and this line: const [values, setValues] = useContext(GlobalContext); will destructure the value (an array) but there is a key difference between destructuring an array and an object: with an array, the destructuring is by order( unlike an object which is by name), meaning: that the values variable will hold the the first object { ...state } which is not iteratable.

If Your value will be an object:

{
    state,
    changeState,
    values,
    setValues,
    radioSelected,
    setRadioSelected,
  }

and the useContext will be const {state,setValues} = useContext(GlobalContext);

  • Related