Skip to content

Instantly share code, notes, and snippets.

@hamidjafari
Created May 29, 2021 20:35
Show Gist options
  • Save hamidjafari/ad000722788243016c5e2606377b82dc to your computer and use it in GitHub Desktop.
Save hamidjafari/ad000722788243016c5e2606377b82dc to your computer and use it in GitHub Desktop.
import React, {
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { useHistory } from "react-router";
type routeStateType = {
[uniqueKey: string]: any;
};
type allSavedStatesType = Map<string[], routeStateType>;
const RouteStatesContext = React.createContext<{
allStates: allSavedStatesType;
setPageStates: (
pageKey: string,
uniqueKey: string,
nextHookState: any
) => void;
getHookState: (pageKey: string, uniqueKey: string) => any;
updateKey: (pageKey: string, newKey: string) => void;
}>(undefined!);
export const RouteStateContextProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [allStatesMap, setStatesMap] = useState<allSavedStatesType>(new Map());
const setPageStates = (
pageKey: string,
uniqueKey: string,
nextHookState: any
) => {
setStatesMap(allStatesMap => {
const mapKeyForPage = getMapKey(pageKey, allStatesMap);
const pageState = mapKeyForPage && allStatesMap.get(mapKeyForPage);
const newPageState = pageState
? { ...pageState, [uniqueKey]: nextHookState }
: { [uniqueKey]: nextHookState };
const newAllStatesMap: allSavedStatesType = new Map();
Array.from(allStatesMap.keys()).forEach(k => {
const val = allStatesMap.get(k);
if (val && k !== mapKeyForPage) {
newAllStatesMap.set(k, val);
}
});
newAllStatesMap.set(mapKeyForPage || [pageKey], newPageState);
return newAllStatesMap;
});
};
const getMapKey = (
pageKey: string,
allStatesMap: allSavedStatesType
): string[] | undefined => {
const keys = Array.from(allStatesMap.keys());
const key = keys.find(k => k.indexOf(pageKey) > -1);
if (!key) return undefined;
return key;
};
const getPageState = (pageKey: string, allStatesMap: allSavedStatesType) => {
const key = getMapKey(pageKey, allStatesMap);
if (!key) return undefined;
const pageState = allStatesMap.get(key);
if (!pageState) return undefined;
return pageState;
};
const getHookState = (pageKey: string, uniqueKey: string) => {
const pageState = getPageState(pageKey, allStatesMap);
if (!pageState) return undefined;
return pageState[uniqueKey];
};
const updateKey = (pageKey: string, newKey: string) => {
setStatesMap(allStatesMap => {
const mapKeyForPage = getMapKey(pageKey, allStatesMap);
if (!mapKeyForPage) return allStatesMap;
const pageState = mapKeyForPage && allStatesMap.get(mapKeyForPage);
if (!pageState) return allStatesMap;
const newAllStatesMap: allSavedStatesType = new Map();
Array.from(allStatesMap.keys()).forEach(k => {
const val = allStatesMap.get(k);
if (val && k !== mapKeyForPage) {
newAllStatesMap.set(k, val);
}
});
newAllStatesMap.set([...mapKeyForPage, newKey], pageState);
return newAllStatesMap;
});
};
return (
<RouteStatesContext.Provider
value={{
allStates: allStatesMap,
setPageStates,
getHookState,
updateKey,
}}
>
{children}
</RouteStatesContext.Provider>
);
};
const useRouteSavedState = <T,>(
uniqueKey: string,
initialState: T
): [T, React.Dispatch<React.SetStateAction<T>>] => {
const { location } = useHistory();
const pageKey = location.key || "initialNoKey";
const firstRef = useRef(pageKey);
const context = useContext(RouteStatesContext);
if (!context) throw new Error("useRouteSaved rendered outside of the router");
const { setPageStates, getHookState, updateKey } = context;
const [savedState, setSavedState] = useState<T>(
getHookState(pageKey, uniqueKey) !== undefined
? getHookState(pageKey, uniqueKey)
: initialState
);
useEffect(() => {
setPageStates(pageKey, uniqueKey, savedState);
}, [savedState]);
useEffect(() => {
updateKey(firstRef.current, pageKey);
}, [pageKey]);
return [savedState, setSavedState];
};
export default useRouteSavedState;
@hamidjafari
Copy link
Author

custom hook for holding state between react router page transitions .
you can use this hook by providing it a unique key in page as first parameter and your desired state .
const [ state, setState ] = useRouteSavedState( "uniqueKey", initialState )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment