skip to Main Content

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


  1. Chosen as BEST ANSWER

    Issue is resolved. I can use

    import router, { useRouter } from 'next/router'
    

  2. You need to ensure that the component from react-router-dom is included at the appropriate level.

    import { useEffect } from 'react';
    import { AppProps } from 'next/app';
    import { useRouter } from 'next/router';
    import { ThemeProvider } from '@emotion/react';
    import { CacheProvider } from '@emotion/react';
    import createEmotionCache from '../src/createEmotionCache';
    import { QueryClient, QueryClientProvider } from 'react-query';
    import { ReactQueryDevtools } from 'react-query/devtools';
    import { Hydrate } from 'react-query/hydration';
    import { Provider as NextAuthProvider } from 'next-auth/client';
    import { ToastContainer } from 'react-toastify';
    import 'react-toastify/dist/ReactToastify.css';
    import '../styles/globals.css';
    import theme from '../src/theme';
    
    const clientSideEmotionCache = createEmotionCache();
    
    function MyApp({ Component, pageProps }: AppProps) {
      const router = useRouter();
      
      useEffect(() => {
        // Remove the server-side injected CSS to avoid conflicts with emotion
        const jssStyles = document.querySelector('#jss-server-side');
        if (jssStyles && jssStyles.parentNode) {
          jssStyles.parentNode.removeChild(jssStyles);
        }
      }, []);
    
      return (
        <QueryClientProvider client={queryClient}>
          <Hydrate state={pageProps.dehydratedState}>
            <CacheProvider value={clientSideEmotionCache}>
              <ThemeProvider theme={theme}>
                <NextAuthProvider session={pageProps.session}>
                  <Router location={router.pathname} locale={router.locale}>
                    <Component {...pageProps} />
                  </Router>
                </NextAuthProvider>
              </ThemeProvider>
              <ReactQueryDevtools initialIsOpen={false} />
              <ToastContainer />
            </CacheProvider>
          </Hydrate>
        </QueryClientProvider>
      );
    }
    
    export default MyApp;
    

    You can adjust your UserProvider component to handle redirections:

    import { createContext, useEffect, useState } from "react";
    import { loginAPI, registerAPI } from "../services/AuthService";
    import { UserProfile } from "../models/User";
    import axios from "axios";
    import { toast } from "react-toastify";
    import { useRouter } from 'next/router'; // Import useRouter hook for Next.js routing
    
    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 router = useRouter(); // Initialize useRouter hook
      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!");
              router.push("/"); // Redirect to dashboard page after login
            }
          })
          .catch((e) => toast.warning("Server error occurred"));
      };
    
      const isLoggedIn = () => {
        return !!user;
      };
    
      return (
        <UserContext.Provider
          value={{ loginUser, user, token, isLoggedIn  }}
        >
          {isReady ? children : null}
        </UserContext.Provider>
      );
    };
    
    export const useAuth = () => React.useContext(UserContext);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search