diff --git a/src/Provider.tsx b/src/Provider.tsx index 21b928ce2..5b9b85108 100644 --- a/src/Provider.tsx +++ b/src/Provider.tsx @@ -5,12 +5,13 @@ import { Provider as ReduxProvider } from 'react-redux'; import { ER_INTERN_FLATE } from './constant'; import FeatureToggle from './moduler/feature/FeatureToggle'; -import createStore from './store'; +import createStore, { RootState } from './store'; interface Props { children: React.ReactNode; setFnrRef?: (setFnr: Dispatch) => void; fnr?: string; + preloadedState?: RootState; } export const ErVeilederContext = React.createContext(false); @@ -22,7 +23,7 @@ export const useFnr = () => useContext(FnrContext); // eslint-disable-next-line @typescript-eslint/no-unused-vars const noOp = (_: string | undefined) => {}; -const Provider = ({ children, setFnrRef, fnr: propFnr }: Props) => { +const Provider = ({ children, setFnrRef, fnr: propFnr, preloadedState }: Props) => { const [fnr, setFnr] = useState(propFnr); useEffect(() => { if (setFnrRef) setFnrRef(setFnr); @@ -33,7 +34,9 @@ const Provider = ({ children, setFnrRef, fnr: propFnr }: Props) => { }; }, []); - const store = useMemo(createStore, [fnr]); + const store = useMemo(() => { + return createStore(preloadedState); + }, [fnr]); return ( diff --git a/src/moduler/dialog/DialogFlateUtils.ts b/src/moduler/dialog/DialogFlateUtils.ts index e431a7622..667cad513 100644 --- a/src/moduler/dialog/DialogFlateUtils.ts +++ b/src/moduler/dialog/DialogFlateUtils.ts @@ -25,7 +25,7 @@ export const byttTilDialogFlate = ({ dialogId: dialogId, aktivitetId: aktivitetId, }, - }) + }), ); }; diff --git a/src/moduler/oppfolging-status/OppfolgingStatus.tsx b/src/moduler/oppfolging-status/OppfolgingStatus.tsx index 9620e2254..e0e51a7e4 100644 --- a/src/moduler/oppfolging-status/OppfolgingStatus.tsx +++ b/src/moduler/oppfolging-status/OppfolgingStatus.tsx @@ -17,6 +17,7 @@ import { } from './oppfolging-selector'; import { hentOppfolging } from './oppfolging-slice'; import VidereSendBrukereEllerRenderChildren from './VidereSendBrukereEllerRenderChildren'; +import { Status } from '../../createGenericSlice'; interface Props { children: ReactNode; @@ -48,8 +49,12 @@ const OppfolgingStatus = ({ children }: Props) => { }; useEffect(() => { - dispatch(hentOppfolging()); - dispatch(hentIdentitet()); + if (avhengigheter[0] === Status.NOT_STARTED) { + dispatch(hentOppfolging()); + } + if (avhengigheter[1] === Status.NOT_STARTED) { + dispatch(hentIdentitet()); + } }, []); return ( diff --git a/src/store.ts b/src/store.ts index 2a27f6865..c0ed8a48b 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,13 +1,46 @@ import { configureStore } from '@reduxjs/toolkit'; import reducer from './reducer'; +import { EnhancedStore } from '@reduxjs/toolkit/src/configureStore'; -const createStore = () => - configureStore({ +let store: EnhancedStore | null = null; +const createStore = (preloadedState: any = undefined) => { + const newStore = configureStore({ reducer: reducer, + preloadedState, }); + store = newStore; + return newStore; +}; type Store = ReturnType; + +const key = 'aktivitetsplan-state'; +export const getPreloadedStateFromSessionStorage = (fnr: string | undefined): RootState | undefined => { + if (!fnr) return undefined; + const serializedState = sessionStorage.getItem(key); + if (serializedState) { + try { + const state: RootState = JSON.parse(serializedState); + // Only use cache if correct user + if (fnr === state.data.oppfolging?.data?.fnr) { + return JSON.parse(serializedState); + } + sessionStorage.removeItem(key); + return undefined; + } catch (e) { + console.warn(e); + return undefined; + } + } + return undefined; +}; +export const saveReduxStateToSessionStorage = () => { + const state = store?.getState(); + sessionStorage.setItem(key, JSON.stringify(state)); +}; +export const clearReduxCache = () => sessionStorage.removeItem(key); + export type RootState = ReturnType; export type Dispatch = Store['dispatch']; diff --git a/src/webcomponentWrapper.tsx b/src/webcomponentWrapper.tsx index 604795b7e..2b5e7ab79 100644 --- a/src/webcomponentWrapper.tsx +++ b/src/webcomponentWrapper.tsx @@ -9,24 +9,32 @@ import modulesCss from './moduler/aktivitet/aktivitet-kort/Aktivitetskort.module import Provider from './Provider'; import tailwindCss from './tailwind.css?inline'; import { createRoot, Root } from 'react-dom/client'; +import { + clearReduxCache, + getPreloadedStateFromSessionStorage, + RootState, + saveReduxStateToSessionStorage, +} from './store'; + +// Clear redux-cache from session storage on page load to make sure new data is fetched +// Cache is only supposed to be used when "jumping" between apps in veilarbpersonflate +clearReduxCache(); export class DabAktivitetsplan extends HTMLElement { setFnr?: (fnr: string) => void; root: Root | undefined; disconnectedCallback() { + saveReduxStateToSessionStorage(); this.root?.unmount(); } connectedCallback() { - // Cant mount on shadowRoot, create a extra div for mounting modal - const shadowDomFirstChild = document.createElement('div'); // This will be app entry point, need to be outside modal-mount node const appRoot = document.createElement('div'); appRoot.id = 'aktivitetsplan-root'; const shadowRoot = this.attachShadow({ mode: 'closed' }); - shadowRoot.appendChild(shadowDomFirstChild); - shadowDomFirstChild.appendChild(appRoot); + shadowRoot.appendChild(appRoot); // Load styles under this shadowDom-node, not root element const styleElem = document.createElement('style'); @@ -34,12 +42,19 @@ export class DabAktivitetsplan extends HTMLElement { shadowRoot.appendChild(styleElem); const fnr = this.getAttribute('data-fnr') ?? undefined; + let preloadedState: RootState | undefined = undefined; if (fnr) { settLocalStorage(LocalStorageElement.FNR, fnr); + preloadedState = getPreloadedStateFromSessionStorage(fnr); } this.root = createRoot(appRoot); this.root.render( - (this.setFnr = setFnr)}> + (this.setFnr = setFnr)} + > , );