Skip to content

Commit

Permalink
docs: persist and connect state with url (#1804)
Browse files Browse the repository at this point in the history
* docs: persist and connect state with url

* ran prettier

* remove line

* Apply suggestions from code review

Co-authored-by: Blazej Sewera <code@sewera.dev>

* from review comments

---------

Co-authored-by: celinecheng <celine.cheng@capitalone.com>
Co-authored-by: Blazej Sewera <code@sewera.dev>
  • Loading branch information
3 people authored May 17, 2023
1 parent 56ab6db commit 286e443
Showing 1 changed file with 99 additions and 4 deletions.
103 changes: 99 additions & 4 deletions docs/guides/connect-to-state-with-url-hash.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: Connect to state with URL hash
title: Connect to state with URL
nav: 12
---

## State is connected with URL hash
## Connect State with URL Hash

If you want to connect state of a store to URL hash, you can create your own hash storage.

Expand Down Expand Up @@ -43,6 +43,101 @@ export const useBoundStore = create(
)
```

## CodeSandbox Demo
### CodeSandbox Demo

https://codesandbox.io/s/zustand-state-with-url-hash-demo-f29b88?file=/src/store/index.ts
<https://codesandbox.io/s/zustand-state-with-url-hash-demo-f29b88?file=/src/store/index.ts>

## Persist and Connect State with URL Parameters (Example: URL Query Parameters)

There are times when you want to conditionally connect the state to the URL.
This example depicts usage of the URL query parameters
while keeping it synced with another persistence implementation, like `localstorage`.

If you want the URL params to always populate, the conditional check on `getUrlSearch()` can be removed.

The implementation below will update the URL in place, without refresh, as the relevant states change.

```ts
import { create } from 'zustand'
import { persist, StateStorage, createJSONStorage } from 'zustand/middleware'

const getUrlSearch = () => {
return window.location.search.slice(1)
}

const persistentStorage: StateStorage = {
getItem: (key): string => {
// Check URL first
if (getUrlSearch()) {
const searchParams = new URLSearchParams(getUrlSearch())
const storedValue = searchParams.get(key)
return JSON.parse(storedValue)
} else {
// Otherwise, we should load from localstorage or alternative storage
return JSON.parse(localStorage.getItem(key))
}
},
setItem: (key, newValue): void => {
// Check if query params exist at all, can remove check if always want to set URL
if (getUrlSearch()) {
const searchParams = new URLSearchParams(getUrlSearch())
searchParams.set(key, JSON.stringify(newValue))
window.history.replaceState(null, null, `?${searchParams.toString()}`)
}

localStorage.setItem(key, JSON.stringify(newValue))
},
removeItem: (key): void => {
const searchParams = new URLSearchParams(getUrlSearch())
searchParams.delete(key)
window.location.search = searchParams.toString()
},
}

let localAndUrlStore = (set) => ({
typesOfFish: [],
addTypeOfFish: (fishType) =>
set((state) => ({ typesOfFish: [...state.typesOfFish, fishType] })),

numberOfBears: 0,
setNumberOfBears: (newNumber) =>
set((state) => ({ numberOfBears: newNumber })),
})

let storageOptions = {
name: 'fishAndBearsStore',
storage: persistentStorage,
}

const useLocalAndUrlStore = create(persist(localAndUrlStore, storageOptions))

export default localAndUrlStore
```

When generating the URL from a component, you can call buildShareableUrl:

```ts
const buildURLSuffix = (params, version = 0) => {
const searchParams = new URLSearchParams()

const zustandStoreParams = {
state: {
typesOfFish: params.typesOfFish,
numberOfBears: params.numberOfBears,
},
version: version, // version is here because that is included with how Zustand sets the state
}

// The URL param key should match the name of the store, as specified as in storageOptions above
searchParams.set('fishAndBearsStore', JSON.stringify(zustandStoreParams))
return searchParams.toString()
}

export const buildShareableUrl = (params, version) => {
return `${window.location.origin}?${buildURLSuffix(params, version)}`
}
```

The generated URL would look like (here without any encoding, for readability):

`https://localhost/search?fishAndBearsStore={"state":{"typesOfFish":["tilapia","salmon"],"numberOfBears":15},"version":0}}`

0 comments on commit 286e443

Please sign in to comment.