Created
May 29, 2021 20:35
-
-
Save hamidjafari/ad000722788243016c5e2606377b82dc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 )