Skip to content

Instantly share code, notes, and snippets.

@oakfang
Created December 31, 2019 12:25
Show Gist options
  • Save oakfang/eb7f896547fc527bac6234d2a1a44ece to your computer and use it in GitHub Desktop.
Save oakfang/eb7f896547fc527bac6234d2a1a44ece to your computer and use it in GitHub Desktop.
Query params sync
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