Source:  Twitter logo

I'm trying to use context for handling pieces of authentication in my app. I was running into issues because I was trying to call useContext outside of my Context.Provider, so I moved the logic to a child component of the provider.

Now I'm getting an error message TypeError: Object is not iterable (cannot read property Symbol(Symbol.iterator)) where I'm calling useContext in the child component. Is the issue really with getting the values from the context or something else?

In app.js

import AuthContextProvider from "./components/context/authContext";
import RegisterRoutes from "./components/routing/registerRoutes";

function App() {
   return (
      <AuthContextProvider>
          <Route
            exact
            path="/register"
            render={(props) => (
              <RegisterRoutes {...props} />
            )}
          />
      </AuthContextProvider>
   )
}

In my authContext.js

import React, { useState, useEffect, createContext } from "react";

export const AuthContext = createContext();

const AuthContextProvider = (props) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const setAuth = (boolean) => {
    setIsAuthenticated(boolean);
  };

//Auth API logic here//
  const apiOptions = {
    url: "users/is-verified",
    method: "GET",
    headers: {
      token: localStorage.token,
    },
  };

  async function isAuth() {
    axios(apiOptions)
      .then((response) => {
        const resData = response.data;
        resData === true ? setIsAuthenticated(true) : setIsAuthenticated(false);
      })
      .catch((error) => {
        console.log(error.response);
      });
  }

  useEffect(() => {
    isAuth();
  }, []);

  return (
    <AuthContext.Provider
      value={[isAuthenticated, setIsAuthenticated, setAuth]}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

export default AuthContextProvider;

In my registerRoutes.js

import React, { useContext } from "react";
import { Redirect } from "react-router-dom";
import Register from "../pages/register";
import AuthContext from "../context/authContext";

function RegisterRoutes(props) {
  const [isAuthenticated, setAuth] = useContext(AuthContext);

  return !isAuthenticated ? (
    <Register {...props} setAuth={setAuth} />
  ) : (
    <Redirect to="/login" />
  );
}

export default RegisterRoutes;

As the error says, the Context.Provider in authContext.js value is not iterable:

<AuthContext.Provider value={[isAuthenticated, setIsAuthenticated, setAuth]}>

The value passed to the provider needs to be an iterable value, in this case, a valid JSON object, instead of the array that you have provided. so, we change it to:

<AuthContext.Provider value={{isAuthenticated, setIsAuthenticated, setAuth}}>

Then you change the reference in registerRoutes.js to correctly consume the new structure:

const [isAuthenticated, setAuth] = useContext(AuthContext);

becomes

const { isAuthenticated, setAuth } = useContext(AuthContext);

Voila! Your Context.Provider value is iterable and you can consume it in your application.

1 users liked answer #0dislike answer #01
Justin Mitchell profile pic
Justin Mitchell

I think this will help you. My solution for accessing data in the context is creating a custom hook.

//localState.js

import { createContext, useState, useContext } from 'react'

const LocalStateContext = createContext()

const LocalStateProvider = LocalStateContext.Provider

function LocalState({children}) {
  const [someState, setSomeState] = useState('')
  const defaultValues = {
    someState, setSomeState
  }
  
  return <LocalStateProvider value={defaultValues}>
            {children}
         </LocalStateProvider>
}

function useLocalState() {
  const all = useContext(LocalStateContext)
  return all
}

export {LocalState, LocalStateContext, useLocalState}

With this code you can wrap your whole app in the LocalState component and access context values by using the new useLocalState hook. For example

import { useLocalState} from './localstate'

const SomeComponent = () => {
  const { someState } = useLocalState()
  
  return (
    ///...whatever you want
  )
}

export default SomeComponent
I think your issue may be that you have put your default values in an array inside of the value object.
1 users liked answer #1dislike answer #11
Sam Roehrich profile pic
Sam Roehrich

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).