Skip to content

Instantly share code, notes, and snippets.

@generalpiston
Last active September 18, 2021 19:12
Show Gist options
  • Save generalpiston/3dfcd808d0ca0e016653a01e5c7b5c84 to your computer and use it in GitHub Desktop.
Save generalpiston/3dfcd808d0ca0e016653a01e5c7b5c84 to your computer and use it in GitHub Desktop.
Hook for storing, removing, and going to next location in search parameters and provides a default. Automatically clears when going to next location.
import { useCallback, useMemo } from 'react'
interface NextActions {
go: () => void
setValue: (next?: string) => void
clear: () => void
status: 'query'
}
interface DefaultNextActions {
go: () => void
status: 'default'
}
interface EmptyNextActions {
go: () => void
status: 'empty'
}
type NextValue = string | undefined
type NextResponseFull = [NextValue, NextActions]
type NextResponseDefault = [NextValue, DefaultNextActions]
type NextResponseEmpty = [null, EmptyNextActions]
type NextResponse = NextResponseFull | NextResponseDefault | NextResponseEmpty
interface NextOptions {
nextParam: string
history: History
location: Location
}
const DefaultNextOptions = {
nextParam: 'next',
history: window.history,
location: window.location
}
export function useNextQuery(options: NextOptions = DefaultNextOptions): [NextValue, NextActions] {
const { history, location, nextParam } = options
const next = useMemo(() => {
const { search } = location
const urlparams = new URLSearchParams(search)
return urlparams.get(nextParam) ?? undefined
}, [nextParam, location])
const go = useCallback(() => {
if (next) {
history.pushState({}, next)
}
}, [next, history])
const clear = useCallback(() => {
const { search } = location
const urlparams = new URLSearchParams(search)
urlparams.delete(nextParam)
const url = `${location.origin}?${urlparams.toString()}`
history.pushState({}, url)
}, [nextParam, history, location])
const setValue = useCallback(
(next) => {
const { search } = location
const urlparams = new URLSearchParams(search)
urlparams.set(nextParam, next)
const url = `${location.origin}?${urlparams.toString()}`
history.pushState({}, url)
},
[nextParam, history, location]
)
return [next, { go, clear, setValue, status: 'query' }]
}
export function useNextDefault(
defaultNext?: string,
options: Pick<NextOptions, 'location' | 'history'> = DefaultNextOptions
): [NextValue, DefaultNextActions] {
const { history } = options
const go = useCallback(() => {
if (defaultNext) {
history.pushState({}, defaultNext)
}
}, [defaultNext, history])
return [defaultNext, { go, status: 'default' }]
}
function useNext(): NextResponseFull | NextResponseEmpty
function useNext(defaultNext: string): NextResponse
/**
* 1. Check search parameters for `next`.
* 2. Use provided default.
*/
function useNext(defaultNext?: string, options: NextOptions = DefaultNextOptions): NextResponse {
const search = useNextQuery(options)
const otherwise = useNextDefault(defaultNext, options)
if (search[0]) {
return search
}
if (otherwise[0]) {
return otherwise
}
return [null, { status: 'empty', go: () => null }]
}
export default useNext
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment