Skip to content

baurine/use-url-state

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@baurine/use-url-state

A very lightweight react hook to manage state in the url.

This hook doesn't depend on any router libs (not like @ahooks/use-state-url), it can be used with none router or any router libs.

Installation

npm install @baurine/use-url-state
# or
pnpm add @baurine/use-url-state
# or
yarn add @baurine/use-url-state

Usage

Step 1

Use UrlStateProvider for each route/page, don't share the same UrlStateProvider for multiple pages.

Do like this:

<Switch>
  <Route path="/list">
    <UrlStateProvider>
      <ListPage>
    </UrlStateProvider>
  </Route>
  <Route path="/detail">
    <UrlStateProvider>
      <DetailPage>
    </UrlStateProvider>
  </Route>
</Switch>

Don't do like this:

<UrlStateProvider>
  <Switch>
    <Route path="/list">
        <ListPage>
    </Route>
    <Route path="/detail">
        <DetailPage>
    </Route>
  </Switch>
</UrlStateProvider>

The UrlStateProvider has default value:

function defCtxVal(): UrlStateCtxValue {
  return {
    urlQuery: new URL(window.location.href).search,
    setUrlQuery(p) {
      const url = new URL(window.location.href)
      window.history.replaceState({}, '', `${url.pathname}?${p}`)
    }
  }
}

The default value is only suitable for working with none router lib. If your app works with a router lib, you need to write a adapter.

Adapter for react-router v5:

import { useLocation, useHistory } from 'react-router-dom'

function ReactRouter5UrlStateProvider(props: { children: React.ReactNode }) {
  const loc = useLocation()
  const history = useHistory()

  return (
    <UrlStateProvider
      value={{
        urlQuery: loc.search,
        setUrlQuery(v) {
          history.replace(`${loc.pathname}?${v}`)
        }
      }}
    >
      {props.children}
    </UrlStateProvider>
  )
}

Adapter for react-router v6:

import { useLocation, useNavigate } from 'react-router-dom'

function ReactRouter6UrlStateProvider(props: { children: React.ReactNode }) {
  const loc = useLocation()
  const navigate = useNavigate()

  return (
    <UrlStateProvider
      value={{
        urlQuery: loc.search,
        setUrlQuery(v) {
          navigate(`${loc.pathname}?${v}`)
        }
      }}
    >
      {props.children}
    </UrlStateProvider>
  )
}

Adapter for tanstack-router v1:

import { useLocation, useNavigate } from '@tanstack/react-router'

function TanStackRouterUrlStateProvider(props: { children: React.ReactNode }) {
  const loc = useLocation()
  const navigate = useNavigate()

  return (
    <UrlStateProvider
      value={{
        urlQuery: loc.searchStr,
        setUrlQuery(v: string) {
          navigate({ to: `${loc.pathname}?${v}` })
        }
      }}
    >
      {props.children}
    </UrlStateProvider>
  )
}

Step 2

Define your own url state hook, use useUrlState() to get query, parse it manually, and set query.

import { useUrlState } from '@baurine/use-url-state'

type ExampleUrlState = Partial<Record<'count', string>>

export function useExampleUrlState() {
  const [queryParams, setQueryParams] = useUrlState<ExampleUrlState>()

  // count
  const count = parseInt(queryParams.count ?? '')
  const setCount = (v?: string) => setQueryParams({ count: v })

  return { count, setCount }
}

Step 3

Use the self defined url state hook in your logic code.

import { useExampleUrlState } from './url-state'

function CountButton() {
  const { count, setCount } = useExampleUrlState()

  return (
    <div className="card">
      Count: <button onClick={() => setCount('5')}>Init</button>{' '}
      {!isNaN(count) && (
        <>
          <button onClick={() => setCount(`${count + 1}`)}>Add</button>{' '}
          <button onClick={() => setCount(`${count - 1}`)}>Subtract</button>{' '}
        </>
      )}
      <button onClick={() => setCount()}>Clear</button>
    </div>
  )
}

function CountValue() {
  const { count } = useExampleUrlState()

  return <div className="card">Count is {count}</div>
}

export function Counter() {
  return (
    <div>
      <h2>@baurine/use-url-state demo</h2>
      <CountButton />
      <CountValue />
    </div>
  )
}

Demo

use-url-state.mp4
20241203-121737.mp4

About

a react hook that make url query as a shared store

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published