When I try to use createContext() the console gives me this error:
App.js:6
Uncaught TypeError: Cannot destructure property 'consoleLogFunction' of '(0 , react__WEBPACK_IMPORTED_MODULE_1__.useContext)(...)' as it is null.
I've seen others asking questions about this here in Stack Overflow but I can't find a solution.
GlobalContext.js
import React from 'react'
import { createContext } from 'react'
export const AppContext = createContext();
function GlobalContext() {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}></AppContext.Provider>
)
}
export default GlobalContext
App.js
import "./index.css";
import { useContext, useEffect } from "react";
import { AppContext } from "./components/GlobalContext";
function App() {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return (
<AppContext>
<div>home</div>
</AppContext>
);
}
export default App;
You don't need to export 'AppContext', creating the provider and exporting that is good enough.
Try this, I've also made a couple of modifications to make it easier to use the context later:
GlobalContext.js
import React from 'react'
import { createContext, useContext } from 'react'
const AppContext = createContext();
function GlobalContext({ children }) {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}>
{ children }
</AppContext.Provider>
)
}
export default GlobalContext;
// This is a helper
export const useGlobalContext = () => useContext(AppContext);
Home.js
import { useEffect } from "react";
import { useGlobalContext } from "./components/GlobalContext";
export const Home = ({ children }) => {
const { consoleLogFunction } = useGlobalContext();
useEffect(() => {
consoleLogFunction();
}, []);
return(
<div>home</div>
)
};
App.js
import "./index.css";
import { useEffect } from "react";
import GlobalContext from "./components/GlobalContext"
import { Home } from "./components/Home";
function App() {
return(
<GlobalContext>
<Home />
</GlobalContext>
);
}
export default App;
hello man the problem is because App component is not wrapped inside GlobalContext . and in the GlobalContext component you should handle the children prop.
it will work when doing it like this example :
import { useEffect, useContext, createContext } from "react";
import "./styles.css";
export const AppContext = createContext();
function GlobalContext(props) {
const consoleLogFunction = () => {
console.log("ok");
};
return (
<AppContext.Provider value={{ consoleLogFunction }}>
{props.children}
</AppContext.Provider>
);
}
const Home = () => {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return <div>home</div>;
};
export default function App() {
return (
<GlobalContext>
<Home />
</GlobalContext>
);
}
Hope this help.
Related
I'm using mobile-detect library and this tutorial:
link
/src/utiles/isMobile.tsx:
import MobileDetect from "mobile-detect";
import { GetServerSidePropsContext } from "next";
export const getIsSsrMobile = (context: GetServerSidePropsContext) => {
const md = new MobileDetect(context.req.headers["user-agent"] as string);
return Boolean(md.mobile());
};
/src/hooks/useIsMobile.js:
import { useContext } from "react";
import { IsSsrMobileContext } from "../contexts/isSsrMobileContext";
export const useIsMobile = () => {
const isSsrMobile = useContext(IsSsrMobileContext);
return isSsrMobile;
};
/src/contexts/isSsrMobileContext.js:
import { createContext } from "react";
export const IsSsrMobileContext = createContext(false);
my `_app.tsx`:
import { IsSsrMobileContext } from "./../src/contexts/isSsrMobileContext";
function MyApp({ Component, pageProps }: AppProps <{ isSsrMobile: boolean }>): JSX.Element {
return (
<IsSsrMobileContext.Provider value={pageProps.isSsrMobile}>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</IsSsrMobileContext.Provider>
);
}
export default MyApp;
inside my pages/index.tsx:
console.log("useIsMobile",useIsMobile())
It shows null.
It's HomePage component of react js
import React from 'react';
import axios from 'axios';
import { useState, useEffect } from 'react';
import { useNavigate,useParams } from 'react-router-dom';
import { Main } from '../components/Main';
import { Controls } from '../components/Controls';
import { ALL_COUNTRIES } from '../config';
import { List } from '../components/List';
import { Card } from '../components/Card';
import { Details } from './Details';
export const HomePage = () => {
const [countries,setCountries] = useState([]);
const navigate = useNavigate();
useEffect(() => {
axios.get(ALL_COUNTRIES).then(({data})=>setCountries(data))
},[]);
return (
<>
<Controls/>
<List>
{
countries.map((c)=>
{
const countryInfo={
img:c.flags.png,
name:c.name,
info: [
{
title:'Population',
description:c.population.toLocaleString(),
},
{
title:'Region',
description:c.region,
},
{
title:'Flag',
description:c.capital,
},
],
};
return <Card key={c.name} onClick={(e)=>{navigate('/country/${c.name}')}}
{...countryInfo}/>
})
}
</List>
</>
);
};
It's second components Details
export const Details = ({match}) => {
return (
<div>
Details {match.param.name}
<div/>
);
};
[https://i.stack.imgur.com/KUyrA.png][before]
HomePage itself looks like this
but when i click on flag/card it sends me on second page as expected but gives me this error
[https://i.stack.imgur.com/JqEHm.png][after]
also i'm using react-router-domV6
and this API https://restcountries.com/v2/all
also both Components are in
Edit:
I was not using
import Link from 'next/link';
in my Header.tsx component.
Now it works.
Don't know what I am doing wrong right here.
I try to make global state (to indicate if user is logged in or not) that just flows through the pages and I try to do it with react's hook useContext.
It is not working like how I would like to make it work. When I toggleLogged and go to another page, the context has default value and not the changed one.
I think the problem I am facing is something really small or a fundemantal thing that I just can't see.
Here is how my UserContext.ts file looks:
import { createContext, useContext } from 'react';
type userContextType = {
isLogged: boolean;
toggleLogged: () => void;
};
const userContextDefault: userContextType = {
isLogged: false,
toggleLogged: () => {},
};
export const UserContext = createContext(userContextDefault);
export function useUserContext() {
return useContext(UserContext);
}
Here is my Layout.tsx:
import React, { useState } from 'react';
import Header from './Header';
const Layout = (props: any) => {
const { children } = props;
return (
<div className='container'>
<Header />
{children}
</div>
);
};
export default Layout;
And lastly here is my _app.tsx:
import type { AppContext, AppProps } from 'next/app';
import Layout from '../components/Layout';
import { useState } from 'react';
import { UserContext } from '../components/UserContext';
import '../styles/globals.css';
const MyApp = ({ Component, pageProps }: AppProps) => {
const [isLogged, setIsLogged] = useState(true);
const toggleLogged = () => {
setIsLogged((isLogged) => !isLogged);
};
return (
<UserContext.Provider value={{ isLogged, toggleLogged }}>
<Layout>
<Component {...pageProps} />;
</Layout>
</UserContext.Provider>
);
};
export default MyApp;
Thanks for any help in advance.
You need to return the provider in the context, and then reference the instance in _app.js.
Here's my AuthContext (as an example). Don't worry about the specific code, but use my implementation as the foundation, and you'll be up and running in no time!
import { createContext, useContext, useEffect, useState } from 'react'
import { auth } from '../firebase'
const AuthContext = createContext()
export function useAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState()
const [loading, setLoading] = useState(true)
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
function signOut() {
return auth.signOut();
}
function signUp(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function getUser() {
return auth.currentUser
}
function isAdmin() {
return auth.currentUser.getIdTokenResult()
.then((idTokenResult) => {
if (!!idTokenResult.claims.admin) {
return true
} else {
return false
}
})
}
function isEditor() {
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
getUser,
login,
signOut,
signUp
}
return (
<AuthContext.Provider value={value}>
{ !loading && children }
</AuthContext.Provider>
)
}
My _app.js file:
import '../styles/globals.scss'
import { motion, AnimatePresence } from 'framer-motion'
import { useRouter } from 'next/router'
import Header from '../components/Header'
import Footer from '../components/Footer'
import { AuthProvider } from '../contexts/AuthContext'
import { CartProvider } from '../contexts/CartContext'
import { ThemeProvider } from '#material-ui/core'
import theme from '../styles/theme'
export default function App({ Component, pageProps }) {
const router = useRouter()
return(
<AnimatePresence exitBeforeEnter>
<CartProvider>
<AuthProvider>
<ThemeProvider theme={theme}>
<Header />
<motion.div key={router.pathname} className="main">
<Component { ...pageProps } />
<Footer />
</motion.div>
</ThemeProvider>
</AuthProvider>
</CartProvider>
</AnimatePresence>
)
}
I have a small React hook that is wrapping a class component and I am trying to pass a state from this hook to the class using useEffect but I keep getting the followwing error;
TypeError: Object(...) is not a function or its return value is not iterable on the following line;
const [{}, dispatch] = useStateValue();
I use the same line in another hook elsewhere in my app so I am not sure why this one is throwing this particular error
This is the complete component code.
If anyone has any ideas why this may not be working I would be most appreciative to hear them.
Thank you.
Component
import React, { useEffect } from 'react';
import App from 'next/app';
import firebase from 'firebase';
import { useStateValue, StateProvider } from '../state';
import { firebaseConfig } from '../constants';
import { initialState, reducer } from '../reducer';
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
function AppWrapper(Component) {
return function WrappedComponent(props) {
const [{}, dispatch] = useStateValue();
useEffect(() => {
console.log('Moiunted');
firebase.auth().onAuthStateChanged((user) => {
dispatch({
type: 'userLogin',
currentUser: user
})
});
}), [];
return <Component {...props} />
}
}
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<StateProvider
initialState={initialState}
reducer={reducer}
>
<Component {...pageProps} />
</StateProvider>
);
}
}
export default AppWrapper(MyApp)
State function
import React, { createContext, useContext, useReducer } from 'react';
export const StateContext = createContext();
export const StateProvider = ({reducer, initialState, children}) => (
<StateContext.Provider
value={useReducer(reducer, initialState)}
>
{children}
</StateContext.Provider>
);
export const useStateValue = () => useContext(StateContext);
I need to use dispatch Context API methods in _app.js.
The main limitation is that I use React hooks along with Context API, since _app.js is a Class, I can't use hooks within it.
My code:
// store.js
import React, { createContext, useContext, useReducer } from "react";
import mainReducer from "../store/reducers";
const AppStateContext = createContext();
const AppDispatchContext = createContext();
const initialState = {
filters: {
diet: {
selected: []
}
}
};
const useAppState = () => useContext(AppStateContext);
const useAppDispatch = () => useContext(AppDispatchContext);
const useApp = () => [useAppState(), useAppDispatch()];
const AppProvider = ({ children }) => {
const [state, dispatch] = useReducer(mainReducer, initialState);
return (
<AppStateContext.Provider value={state}>
<AppDispatchContext.Provider value={dispatch}>
{children}
</AppDispatchContext.Provider>
</AppStateContext.Provider>
);
};
export { AppProvider, useAppState, useAppDispatch, useApp };
// _app.js
import App from "next/app";
import React from "react";
import { AppProvider } from "../store";
class MyApp extends App {
componentDidMount() {
/***********************************/
// HERE I WOULD LIKE TO USE DISPATCH
/***********************************/
}
render() {
const { Component, router, pageProps } = this.props;
return (
<AppProvider>
<Component {...pageProps} />
</AppProvider>
);
}
}
export default MyApp;
If you really want to use hooks, then just put a wrapper around _app.js like this:
import React from 'react'
import App from 'next/app'
function MyComponent({ children }) {
// You can use hooks here
return <>{children}</>
}
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<MyComponent>
<Component {...pageProps} />
</MyComponent>
)
}
}
export default MyApp