Created
December 31, 2019 12:25
-
-
Save oakfang/eb7f896547fc527bac6234d2a1a44ece to your computer and use it in GitHub Desktop.
Query params sync
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, { | |
useState, | |
useCallback, | |
useLayoutEffect, | |
createContext, | |
useContext, | |
useMemo, | |
} from 'react'; | |
import qs from 'qs'; | |
import { useLocation, useHistory } from 'react-router-dom'; | |
function useQueryParams() { | |
let { search } = useLocation(); | |
if (search) { | |
search = search.substr(1); | |
} | |
return qs.parse(search); | |
} | |
function updateUrl(history, op, pathname, params, hash) { | |
let search = qs.stringify(params); | |
if (search) { | |
search = '?' + search; | |
} | |
const location = { pathname, search, hash }; | |
history[op](location); | |
return params; | |
} | |
function useQueryParamsState(initialParams = {}) { | |
const history = useHistory(); | |
let { pathname, hash, search } = useLocation(); | |
const [paramsState, setParams] = useState(() => { | |
if (search) { | |
search = search.substr(1); | |
return qs.parse(search); | |
} | |
return updateUrl(history, 'replace', pathname, initialParams, hash); | |
}); | |
useLayoutEffect(() => { | |
const { location } = history; | |
const { hash, pathname } = location; | |
updateUrl(history, 'push', pathname, paramsState, hash); | |
}, [paramsState, history]); | |
useLayoutEffect(() => { | |
if (search) return; | |
setParams(paramsState => { | |
return updateUrl(history, 'replace', pathname, paramsState, hash); | |
}); | |
}, [pathname, history, hash, search]); | |
const setParam = useCallback((param, value) => { | |
setParams(params => ({ ...params, [param]: value })); | |
}, []); | |
const params = useQueryParams(); | |
return { params, setParam }; | |
} | |
const QueryContext = createContext(); | |
export function createQueryParamState( | |
param, | |
parse = x => x, | |
stringify = x => x | |
) { | |
return function useQueryParamState() { | |
const { params, setParam } = useContext(QueryContext); | |
const value = useMemo(() => parse(params[param]), [params]); | |
const set = useCallback( | |
value => { | |
setParam(param, stringify(value)); | |
}, | |
[setParam] | |
); | |
return [value, set]; | |
}; | |
} | |
export function QueryProvider({ initialParams, children }) { | |
const ctx = useQueryParamsState(initialParams); | |
return <QueryContext.Provider value={ctx}>{children}</QueryContext.Provider>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment