Cannot destructure property of object from context
Asked Answered
S

9

10

Re-posting a similar question to my last because of a new issue. I'm trying to use context with hooks to manage authentication in my react app. I'm getting the error TypeError: Cannot destructure property 'isAuthenticated' of 'Object(...)(...)' as it is undefined., yet when I console.log the property where it's defined, I see false not undefined.

I have the context definition and provider in authContext.js

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

const AuthContext = createContext();
export { AuthContext };

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

  const setAuth = (boolean) => {
    setIsAuthenticated(boolean);
  };

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

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

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

export default AuthContextProvider;

Then I have my routes wrapped in the provider in app.js

import React from "react";
import {
  Switch,
  Route,
} from "react-router-dom";
import "./App.css";
import Register from "./components/pages/register";
import AuthContextProvider from "./components/context/authContext";
import RegisterRoutes from "./components/routing/registerRoutes";

function App() {

  return (
    <AuthContextProvider>
      <div className="App h-100 ">
        <Switch>
          <Route
            exact
            path="/register"
            render={(props) => (
              <RegisterRoutes {...props} />
            )}
          />
        </Switch>
      </div>
    </AuthContextProvider>
  );
}

export default App;

Then I have a RegisterRoutes component that returns one of two pages based on the isAuthenticated value

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

function RegisterRoutes(props) {
  const { isAuthenticated, setAuth } = useContext(AuthContext);
  console.log(isAuthenticated);

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

export default RegisterRoutes;
Sheepherder answered 14/6, 2020 at 22:8 Comment(0)
A
11

Your default export is AuthContextProvider (a component), not AuthContext (a context object): that won't work as you expect. Furthermore you are trying to export the context object inside another object:

// WRONG: export context inside {}
const AuthContext = createContext();
export { AuthContext };

Solution 1

Instead export the context variable normally (not as default):

// Export the variable
export const AuthContext = createContext();

// This works
import { AuthContext } from "../context/authContext";

Solution 2 (recommended)

A better practice is to keep the context in a separate file and export it as default:

// File AuthContext.js

import React, { createContext } from "react";

const AuthContext = createContext();

export default AuthContext;

// Import it in another file
import AuthContext from "./AuthContext.js";
Astonishment answered 14/6, 2020 at 22:22 Comment(1)
Thank you! I implemented your solution 2 and it works. That makes sense and I like keeping the two in separate files.Sheepherder
U
6

I solved this by doing a simple thing. In index, react-app, just put my provider involving my App.

ReactDOM.render(
  <React.StrictMode>
    <AuthProvider>
      <App />
    </AuthProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

I have the same problem when I try set logging in localStorage.

Ulpian answered 26/1, 2021 at 3:44 Comment(0)
D
2

For people using gatsby and context API in the page component:

you need to wrap root element with context provider in gatsby-browser.js and in gatsby-ssr.js.

Example:

import React from 'react';

import { CustomProvider } from './src/context/CustomProvider';

export const wrapRootElement = ({ element }) => {
  return <CustomProvider>{element}</CustomProvider>;
};
Den answered 27/1, 2021 at 4:5 Comment(0)
E
0

Try in your App.js define the value = {{state, dispatch}} in provider

 <UserContext.Provider value ={{state, dispatch}}>
        <Routing/>
      </UserContext.Provider>
Evertor answered 7/10, 2021 at 11:40 Comment(0)
A
0

you must put this {} for wrape the context during the send in other file

import {AuthContext} from "../context/authContext";

import { Redirect } from "react-router-dom";

import Register from "../pages/register";

function RegisterRoutes(props) {

  const { isAuthenticated, setAuth } = useContext(AuthContext);
Alfie answered 7/12, 2021 at 17:28 Comment(0)
O
0

enter image description here

This is the way I solved it, wrapping all the component in app.js file with authProvider.

Ocieock answered 1/1, 2022 at 2:16 Comment(0)
B
0

My error was resolved once I added the AuthState in my App.js file.

import { Routes, Route } from "react-router-dom";
import { BrowserRouter as Router } from "react-router-dom";
import Home from "./screens/Home";
import "./index.css";

import Register from "./components/Register";
import Login from "./components/Login";
import AuthState from "./context/auth/AuthState";
function App() {
  return (
    <>
      <AuthState>
          <Router>
            <Navbar />
            <Routes>
              <Route exact path="/" element={<Home />} />
              <Route exact path="/register" element={<Register />} />
              <Route exact path="/login" element={<Login />} />
            </Routes>
          </Router>
      </AuthState>
    </>
  );
}

export default App;
Bascomb answered 28/3, 2023 at 17:0 Comment(0)
W
0

In my case, I was using a custom hook with a property dependent on useContext which I was never exposing to the route. Don't forget to wrap your components with context providers!

Weyermann answered 9/9, 2023 at 1:2 Comment(0)
R
0

This is works for me

    <BrowserRouter>
    <AppContext>
        <Header/>
        <Routes>
            <Route path="/" element={<Home/>} />
            <Route path="/category/:id" element={<Category />} />
            <Route path="/product/:id" element={<SingleProduct />} />
        </Routes>
        <Newsletter />
        <Footer />
    </AppContext>
</BrowserRouter>
Retard answered 7/12, 2023 at 6:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.