UNPKG

use-state-in-url

Version:

React hook to keep state in URL for deep linking

69 lines 2.77 kB
import { useCallback, useEffect, useRef, useState } from "react"; import { deserialize, serialize } from "./serializeParams"; import { parseSearch, buildSearch, isEqual } from "./urlUtils"; function normalizeParam(param) { return typeof param === "string" ? { name: param, defaultValue: "" } : param; } function getParamValue(param, searchParams) { const value = searchParams.get(param.name); return value !== undefined && value !== null ? deserialize(param.defaultValue, value) : param.defaultValue; } export function useStateInUrl(param, opts) { const { location, navigate } = opts; const locationRef = useRef(location); const config = useRef(normalizeParam(param)); const getValue = () => { const searchParams = parseSearch(location.search); return getParamValue(config.current, searchParams); }; const [value, setValue] = useState(getValue); useEffect(() => { locationRef.current = location; const newValue = getValue(); if (!isEqual(newValue, value)) { setValue(newValue); } }, [location.search, location.pathname]); const createUpdater = useCallback((newValue) => (params) => { setValue(newValue); if (newValue === undefined || isEqual(newValue, config.current.defaultValue)) { params.delete(config.current.name); } else { const serialized = serialize(config.current.defaultValue, newValue); params.set(config.current.name, serialized); } return params; }, []); const update = useCallback((newValue) => { const params = parseSearch(locationRef.current.search); createUpdater(newValue)(params); const search = buildSearch(params); navigate(`${locationRef.current.pathname}${search}`); }, [createUpdater, navigate]); return [value, update, createUpdater]; } export function useBatchUpdate(opts) { const { location, navigate } = opts; const locationRef = useRef(location); useEffect(() => { locationRef.current = location; }, [location]); const batchUpdate = useCallback((updates, options = {}) => { const { includeExisting = true, path } = options; let params = includeExisting ? parseSearch(locationRef.current.search) : new Map(); params = updates.reduce((acc, update) => update(acc), params); const search = buildSearch(params); const pathname = path || locationRef.current.pathname; navigate(`${pathname}${search}`); }, [navigate]); return { batchUpdate }; } export { parseSearch as parseQueryString, buildSearch as createQueryString }; //# sourceMappingURL=useStateInUrl.js.map