Uncaught TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)) in my React project
Asked Answered
C

4

10

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;
Cardialgia answered 12/2, 2022 at 10:11 Comment(0)
B
1

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);
Becalmed answered 12/2, 2022 at 10:19 Comment(0)
H
9

If you are writing your own React hooks (use... functions), then this can happen based on how you are returning your values in the custom hook.

For example, returning an array expects an array:

const [count, setCount, increment] = useCount()
// useCount should return:
return [count, setCount, increment]

Just as well, returning an object requires an object:

const {count, setCount, increment} = useCount()
// useCount should return:
return {count, setCount, increment}

Any intermixing of the two will lead to errors like:

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

For example:

const [count, setCount, increment] = useCount()
// useCount will error if it returns:
return {count, setCount, increment}
Heavyarmed answered 8/7, 2022 at 16:31 Comment(0)
C
3

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);

Choreograph answered 12/2, 2022 at 10:20 Comment(0)
B
1

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);
Becalmed answered 12/2, 2022 at 10:19 Comment(0)
C
1

I encountered this confusing error message when trying to spread a value that is unexpectedly undefined. e.g.

let abc = undefined
let def = [...abc]
Calvo answered 4/6, 2023 at 23:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.