How to keep Context API state when routing between pages in nextjs?
Asked Answered
C

1

7

I have a project with nextJs and typescript. I used context API for handling global states in my application. My problem is when I navigate between pages my state revaluates again.(i used nextjs Link tag) in other words, I want my context updated only once and not each time I navigate between pages. this is my context

import React, {createContext, useState} from "react";

export type ResourcesType =
    'ALL'
    | 'ORDERS'
    | 'ORDERS_CANCELLATION'
    | 'ORDERS_PARTIAL_PAID';

export type AccessType = 'ACT' | 'READ' | 'UPDATE' | 'CREATE' | 'DELETE' | 'All';

export type AccessLevel = 'DO' | 'REQUEST' | 'APPROVE';

export type PermissionType = {
    [key1 in ResourcesType]: {
        [key2 in AccessType]: AccessLevel
    }
} | null;

const PermissionsContext = createContext<{
    permissions: PermissionType,
    setPermissionsHandler: (permissions: PermissionType) => void

}>({
    permissions: null,
    setPermissionsHandler: (permissions: PermissionType) => {}
});


export function PermissionsProvider(props: any) {
    const [permissions, setPermissions] = useState<PermissionType>(null);

    const setPermissionsHandler = (permissions: PermissionType) => {
        setPermissions(permissions);
    }

    const context = {
        permissions,
        setPermissionsHandler
    }
    return <PermissionsContext.Provider value={context}>
        {props.children}
    </PermissionsContext.Provider>
}

export default PermissionsContext;

and I call setPermissionsHandler in my header component and my header component is in all pages.

this is my header component

const Header = () => {
    const permissionsCtx = useContext(PermissionsContext);
    const initialCtx = useContext(InitialContext);

        useEffect(() => {
            console.log(permissionsCtx.permissions,'INN');
            if (!permissionsCtx.permissions) {
                console.log('OH')
                const initialInstance = new HandleInitialize();
                initialInstance.getInitialData()
                    .then(res => {
                        permissionsCtx.setPermissionsHandler(res.data.result.permissions);
                    })
                    .catch(e => console.log(e));
            }
        }, []);

    return (
      <div>
      Hi
        </div>
    );
};

import type {AppProps} from "next/app";
import useRouterStatus from "../hooks/useRouterStatus";
import PrimeReact from "primereact/api";
import "nprogress/nprogress.css";
/*import "primereact/resources/themes/lara-light-indigo/theme.css";*/
import "../styles/override-theme.css";
import "primereact/resources/primereact.min.css";
import "primeicons/primeicons.css";
import "tailwindcss/tailwind.css";
import "../styles/typography.css";
import "../styles/globals.css";
import "../styles/primefaces-overrides.css";
import {PermissionsProvider} from "../store/persmissions";
import {InitialProvider} from "../store/initial";
import {useEffect} from "react";
import {HandleInitialize} from "../adapter/HandleInitialize";

function MyApp({Component, pageProps}: AppProps) {

    return <PermissionsProvider>
            <Component {...pageProps} />
    </PermissionsProvider>;
}

export default MyApp;
Chelseachelsey answered 24/1, 2022 at 7:15 Comment(1)
I just found your question after asking the exact same thing. #71239253 Did you find a solution?Doxy
S
1

Seems to be a black spot.

Like Neutrino above in the comments I am hitting this issue too.

It didn't bother me for ages as on each page I got all app data so didn't notice.

Now different pages have different data and when I use router.push() the new page props don't update the context because the context wrapper is in _app which does not get unmounted and useReducer works like useState. When the page changes you would need to set state or use a reducer to change the context state.

So we have a few options here

1:Use a a:tag so as the app refreshes when changing page next/link. This appears to be the best solution. Also router.refresh() can behave like next/link

2:When you change a page we compare the current context state (stringify) with the incoming state/props and if different set context. We also also have some hybrid current state vs incoming props and set context where appropriate. Very messy and bad way i feel.

3:Instead of putting the context wrapper inside the _app, instead put inside each page component. So when we use router.push() and new context is unmounted/mounted

Daniel

Sweltering answered 13/7, 2022 at 19:13 Comment(2)
Can you elaborate on your first point? I think you missed a word.Huh
Sorry I meant to say a tag, and wrote <a> and it parsed out the <> leaving a.Sweltering

© 2022 - 2024 — McMap. All rights reserved.