Sy3d
Sy3d

Reputation: 269

How can I access my context API state in react app.js file

Hi I am managing my react app state with Context API, and I am able to destructure of the state and functions of my Context in all files except my app.js file.

Here I created a reusable file called 'createDataContext' to prevent writing duplicate code when creating a Context and Provider for each Context.

import React, { createContext, useReducer } from 'react';

export default (reducer, actions, initialState) => {
  const Context = createContext();

  const Provider = ({ children }) => {
    const [state, dispatch] = useReducer(reducer, initialState);

    const boundActions = {};
    for(let key in actions){
      boundActions[key] = actions[key](dispatch);
    }

    return (
      <Context.Provider value={{ state, ...boundActions }}>
        { children }
      </Context.Provider>
    )
  }

  return { Context, Provider }
}

Here is my AuthContext which uses createDataContext file

import createDataContext from "./createDataContext";
import api from '../api/api'

const authReducer = (state, action) => {
  switch(action.type){
    case 'signin':
      return { errorMessage: '', token: action.payload };
    case 'add_error':
      return { ...state, errorMessage: action.payload };
    default:
      return state;
  }
}

const login = dispatch => {
  return async (email, password) => {
    try {
      const res = await api.post('/users/login', { email, password });
      localStorage.setItem('token', res.data.token);
      dispatch({ type: 'signin', payload: res.data.token });
    }catch(err){
      dispatch({ type: 'add_error', payload: 'signin failed' })
    }
  }
}

export const { Context, Provider } = createDataContext(authReducer, { login }, {
  token: null,
  errorMessage: ''
});

I am able to use the context and destructor off the state and function like login in my pages and components but unable to do so in my app.js file.

import React, { useContext } from 'react';
import Home from './pages/home/Home';
import Login from './pages/login/Login';
import Profile from './pages/profile/Profile';
import Register from './pages/register/Register';
import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom';
import Navbar from './components/navbar/Navbar';
import { Provider as AuthProvider } from './context/AuthContext';
import { Context } from './context/AuthContext';

function App() {
  const { state, login } = useContext(Context);

  return (
    <AuthProvider>
      <Router>
        <Navbar />
        <Switch>
          <Route exact path="/">
            <Home />
          </Route>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/register">
            <Register />
          </Route>
          <Route path="/profile/:id">
            <Profile />
          </Route>
        </Switch>
      </Router>
    </AuthProvider>
  );
}

export default App;

I am getting error TypeError: Cannot destructure property 'state' of 'Object(...)(...)' as it is undefined. Can someone please point out what I'm or where I'm going wrong. Thanks.

Upvotes: 0

Views: 1393

Answers (1)

amaster
amaster

Reputation: 2163

You cannot use the context in the same component where it is being provided. To get around this create another new component that is a wrapper for the rest of the children of the Provider

// AppWrapper.jsx
import React from 'react'
import App from './app'
import { Provider as AuthProvider } from './context/AuthContext'

const AppWrapper = () => {
  return (
    <AuthProvider>
      <App />
    </AuthProvider>
  )
}

default export AppWrapper

Then remove the <AuthProvider> from your app.js component, and wherever you were calling <App /> in the tree, call the <AppWrapper /> instead. Then inside of the App you can use the Context like you are trying to do.

Upvotes: 1

Related Questions