How to provide context from contextApi for a specific set of routes in nextjs and preserve state with routing through linking?

Viewed 665

I am using contextApi with nextjs and I'm having some trouble when providing a context just for certain routes. I am able to make the context available for just a few routes, but when I transition from one to the other through linking, I end up losing the state of my application.

I have three files inside my pages folder: index.tsx, Dashboard/index.tsx and SignIn/index.tsx.

If I import the provider inside the files Dashboard/index.tsx and SignIn/index.tsx and go from one page to the other by pressing a Link component from next/link, the whole state is set back to the initial state.

The content of the Dashboard/index.tsx file

    import React from 'react';
    import Dashboard from '../../app/views/Dashboard';
    import { AuthProvider } from '../../contexts/auth';
    const Index: React.FC = () => (
      <AuthProvider>
        <Dashboard />
      </AuthProvider>
    );
    export default Index;

This is the contend of the SignIn/index.tsx file:

    import React from 'react';
    import SignIn from '../../app/views/SignIn';
    import { AuthProvider } from '../../contexts/auth';
    const Index: React.FC = () => (
      <AuthProvider>
        <SignIn />
      </AuthProvider>
    );
    export default Index;

The views folder is where I create the components that will be rendered. The content of the file views/SignIn/index.tsx is:

    import React, { useContext } from 'react';
    import Link from 'next/link';
    import { AuthContext } from '../../../contexts/auth';
    const SignIn: React.FC = () => {
      const { signed, signIn } = useContext(AuthContext);
      async function handleSignIn() {
        signIn();
      }
      return (
        <div>
          <Link href="Dashboard">Go back to Dashboard</Link>
          <button onClick={handleSignIn}>Click me</button>
        </div>
      );
    };
    export default SignIn;

And the content of the file views/Dashboard/index.tsx is:

    import React, { useContext } from 'react';
    import Link from 'next/link';
    import { AuthContext } from '../../../contexts/auth';
    const Dashboard: React.FC = () => {
      const { signed, signIn } = useContext(AuthContext);
      async function handleSignIn() {
        signIn();
      }
      return (
        <div>
          <Link href="SignIn">Go back to sign in page</Link>
          <button onClick={handleSignIn}>Click me</button>
        </div>
      );
    };
    export default Dashboard;

I am able to access the context inside both /Dashboard and /SignIn, but when I press the link, the state comes back to the initial one. I figured out that the whole provider is rerenderized and therefore the new state becomes the initial state, but I wasn't able to go around this issue in a "best practices manner".

If I put the provider inside _app.tsx, I can maintain the state when transitioning between pages, but I end up providing this state to the / route as well, which I am trying to avoid.

I was able to go around this by doing the following, but it really does not seem to be the best solution for me.

I removed the Providers from Pages/SignIn/index.tsx and Pages/Dashboard/index.tsx and used the following snippet for the _app.tsx file:

    import React from 'react';
    import { AppProps } from 'next/app';
    import { useRouter } from 'next/router';
    import { AuthProvider } from '../contexts/auth';
    const App: React.FC<AppProps> = ({ Component, pageProps }) => {
      const router = useRouter();
      const AuthProviderRoutes = ['/SignIn', '/Dashboard'];
      return (
        <>
          {AuthProviderRoutes.includes(router.pathname) ? (
            <AuthProvider>
              <Component {...pageProps} />
            </AuthProvider>
          ) : <Component {...pageProps} />}
        </>
      );
    };
    export default App;

Does anyone have a better solution?

0 Answers
Related