Reputation: 269
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
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