I am using react typescript. And I have a userContext for register users and login.
But if I try to wrap the context in the _app.tsx file. Then I get this error:
error - Error: useNavigate() may be used only in the context of a <Router> component.
So this is part of the UserProvider:
/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */
import { createContext, useEffect, useState } from "react";
import { loginAPI, registerAPI } from "../services/AuthService";
import React from "react";
import { UserProfile } from "../models/User";
import axios from "axios";
import { toast } from "react-toastify";
import { useNavigate } from "react-router-dom";
type UserContextType = {
user: UserProfile | null;
token: string | null;
loginUser: (username: string, password: string) => void;
isLoggedIn: () => boolean;
};
type Props = { children: React.ReactNode };
const UserContext = createContext<UserContextType>({} as UserContextType);
export const UserProvider = ({ children }: Props) => {
const navigate = useNavigate();
const [token, setToken] = useState<string | null>(null);
const [user, setUser] = useState<UserProfile | null>(null);
const [isReady, setIsReady] = useState(false);
useEffect(() => {
const user = localStorage.getItem("user");
const token = localStorage.getItem("token");
if (user && token) {
setUser(JSON.parse(user));
setToken(token);
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
}
setIsReady(true);
}, []);
const loginUser = async (username: string, password: string) => {
await loginAPI(username, password)
.then((res) => {
if (res) {
localStorage.setItem("token", res?.data.token);
const userObj = {
username: res?.data.username,
email: res?.data.email,
};
localStorage.setItem("user", JSON.stringify(userObj));
setToken(res?.data.token!);
setUser(userObj!);
toast.success("Login Success!");
navigate("/");
}
})
.catch((e) => toast.warning("Server error occured"));
};
const isLoggedIn = () => {
return !!user;
};
return (
<UserContext.Provider
value={{ loginUser, user, token, isLoggedIn }}
>
{isReady ? children : null}
</UserContext.Provider>
);
};
export const useAuth = () => React.useContext(UserContext);
And the error apears on this line:
const navigate = useNavigate();
And then I try to wrap the UserProvider in the _app.tsx file:
/* eslint-disable lines-around-comment */
// ** React Perfect Scrollbar Style
import 'react-perfect-scrollbar/dist/css/styles.css'
// ** Global css styles
import '../../styles/globals.css'
import "react-toastify/dist/ReactToastify.css"
// ** Contexts
import { SettingsConsumer, SettingsProvider } from 'src/context/settingsContext'
import type { AppProps } from 'next/app'
// ** Emotion Imports
import { CacheProvider } from '@emotion/react'
import type { EmotionCache } from '@emotion/cache'
// ** Next Imports
import Head from 'next/head'
// ** Loader Import
import NProgress from 'nprogress'
import type { NextPage } from 'next'
import { Router } from 'next/router'
import ThemeComponent from 'src/@core/theme/ThemeComponent'
// ** Component Imports
import UserLayout from 'src/layouts/UserLayout'
import { UserProvider } from 'src/context/useAuth'
// ** Utils Imports
import { createEmotionCache } from 'src/@core/utils/create-emotion-cache'
// ** Config Imports
import themeConfig from 'src/configs/themeConfig'
// ** Extend App Props with Emotion
type ExtendedAppProps = AppProps & {
Component: NextPage
emotionCache: EmotionCache
}
const clientSideEmotionCache = createEmotionCache()
// ** Pace Loader
if (themeConfig.routingLoader) {
Router.events.on('routeChangeStart', () => {
NProgress.start()
})
Router.events.on('routeChangeError', () => {
NProgress.done()
})
Router.events.on('routeChangeComplete', () => {
NProgress.done()
})
}
// ** Configure JSS & ClassName
const App = (props: ExtendedAppProps) => {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props
// Variables
const getLayout = Component.getLayout ?? (page => <UserLayout>{page}</UserLayout>)
return (
<>
<UserProvider>
<CacheProvider value={emotionCache}>
<Head>
<title>{`${themeConfig.templateName} - Material Design React Admin Template`}</title>
<meta
name='description'
content={`${themeConfig.templateName} – Material Design React Admin Dashboard Template – is the most developer friendly & highly customizable Admin Dashboard Template based on MUI v5.`}
/>
<meta name='keywords' content='Material Design, MUI, Admin Template, React Admin Template' />
<meta name='viewport' content='initial-scale=1, width=device-width' />
</Head>
<SettingsProvider>
<SettingsConsumer>
{({ settings }) => {
return <><ThemeComponent settings={settings}>{getLayout(<Component {...pageProps} />)}</ThemeComponent></>
}}
</SettingsConsumer>
</SettingsProvider>
</CacheProvider>
</UserProvider>
</>
)
}
export default App
But if I try this I get the error:
Error: useNavigate() may be used only in the context of a <Router> component.
And I checked the different versions. And it returned the same versions:
yarn list v1.22.19
warning Filtering by arguments is deprecated. Please use the pattern option instead.
├─ [email protected]
└─ [email protected]
Question: how to resolve this error?
2
Answers
Issue is resolved. I can use
You need to ensure that the component from react-router-dom is included at the appropriate level.
You can adjust your UserProvider component to handle redirections: