import * as React from 'react' ; import { DecodedValueMap , EncodedQuery , encodeQueryParams , QueryParamConfigMap , EncodedValueMap , } from 'serialize-query-params' ; import { getSSRSafeSearchString , useUpdateRefIfShallowNew } from './helpers' ; import { useLocationContext } from './LocationProvider' ; import { sharedMemoizedQueryParser } from './memoizedQueryParser' ; import shallowEqual from './shallowEqual' ; import { SetQuery , UrlUpdateType } from './types' ; type ChangesType < DecodedValueMapType > = Partial < DecodedValueMapType > | ( ( latestValues : DecodedValueMapType ) => Partial < DecodedValueMapType > ) ; /**
 * Helper to get the latest decoded values with smart caching.
 * Abstracted into its own function to allow re-use in a functional setter (#26)
 */ function getLatestDecodedValues < QPCMap extends QueryParamConfigMap > ( location , paramConfigMap , paramConfigMapRef , parsedQueryRef , encodedValuesCacheRef , decodedValuesCacheRef ) { const hasNewParamConfigMap = ! shallowEqual ( paramConfigMapRef . current , paramConfigMap ) ; const parsedQuery = sharedMemoizedQueryParser ( getSSRSafeSearchString ( location ) ) ; const hasNewParsedQuery = parsedQueryRef . current !== parsedQuery ; if ( ! hasNewParsedQuery && ! hasNewParamConfigMap && encodedValuesCacheRef . current !== undefined ) { return { encodedValues : encodedValuesCacheRef . current , decodedValues : decodedValuesCacheRef . current , } ; } const encodedValuesCache = encodedValuesCacheRef . current || { } ; const decodedValuesCache = decodedValuesCacheRef . current || { } ; const encodedValues = { } ; const paramNames = Object . keys ( paramConfigMap ) ; const decodedValues = { } ; for ( const paramName of paramNames ) { const paramConfig = paramConfigMap [ paramName ] ; const hasNewEncodedValue = ! shallowEqual ( encodedValuesCache [ paramName ] , parsedQuery [ paramName ] ) ; let encodedValue ; let decodedValue ; if ( hasNewEncodedValue || ( encodedValuesCache [ paramName ] === undefined && decodedValuesCache [ paramName ] === undefined ) ) { encodedValue = parsedQuery [ paramName ] ; decodedValue = paramConfig . decode ( encodedValue ) ; } else { encodedValue = encodedValuesCache [ paramName ] ; decodedValue = decodedValuesCache [ paramName ] ; } encodedValues [ paramName as keyof QPCMap ] = encodedValue ; decodedValues [ paramName as keyof QPCMap ] = decodedValue ; } const hasNewDecodedValues = ! shallowEqual ( decodedValuesCacheRef . current , decodedValues , paramConfigMap ) ; return { encodedValues , decodedValues : hasNewDecodedValues ? decodedValues : decodedValuesCacheRef . current , } ; } /**
 * Given a query parameter configuration (mapping query param name to { encode, decode }),
 * return an object with the decoded values and a setter for updating them.
 */ export const useQueryParams = < QPCMap extends QueryParamConfigMap > ( paramConfigMap ) : [ DecodedValueMap < QPCMap > , SetQuery < QPCMap > ] => { const { location , getLocation , setLocation } = useLocationContext ( ) ; const parsedQuery = sharedMemoizedQueryParser ( getSSRSafeSearchString ( location ) ) ; const paramConfigMapRef = React . useRef ( paramConfigMap ) ; const parsedQueryRef = React . useRef ( parsedQuery ) ; const encodedValuesCacheRef = React . useRef < Partial < EncodedValueMap < QPCMap > > | undefined > ( undefined ) ; const decodedValuesCacheRef = React . useRef < Partial < DecodedValueMap < QPCMap > > > ( { } ) ; paramConfigMap = shallowEqual ( paramConfigMap , paramConfigMapRef . current ) ? paramConfigMapRef . current : paramConfigMap ; const { encodedValues , decodedValues } = getLatestDecodedValues ( location , paramConfigMap , paramConfigMapRef , parsedQueryRef , encodedValuesCacheRef , decodedValuesCacheRef ) ; useUpdateRefIfShallowNew ( parsedQueryRef , parsedQuery ) ; useUpdateRefIfShallowNew ( paramConfigMapRef , paramConfigMap ) ; useUpdateRefIfShallowNew ( encodedValuesCacheRef , encodedValues ) ; useUpdateRefIfShallowNew ( decodedValuesCacheRef , decodedValues , ( a , b ) => shallowEqual ( a , b , paramConfigMap ) ) ; const setQueryDeps = { paramConfigMap , setLocation , getLocation , } ; const setQueryDepsRef = React . useRef ( setQueryDeps ) ; setQueryDepsRef . current = setQueryDeps ; const setQuery = React . useCallback ( ( changes , updateType ? ) => { const deps = setQueryDepsRef . current ; let encodedChanges ; if ( typeof changes === 'function' ) { const { decodedValues : latestValues } = getLatestDecodedValues ( deps . getLocation ( ) , deps . paramConfigMap , paramConfigMapRef , parsedQueryRef , encodedValuesCacheRef , decodedValuesCacheRef ) ; decodedValuesCacheRef . current = latestValues ; encodedChanges = encodeQueryParams ( deps . paramConfigMap , ( changes as Function ) ( latestValues ) ) ; } else { encodedChanges = encodeQueryParams ( deps . paramConfigMap , changes ) ; } deps . setLocation ( encodedChanges , updateType ) ; } , [ ] ) ; return [ decodedValues as DecodedValueMap < QPCMap > , setQuery ] ; } ; export default useQueryParams ;