-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
619c658
commit 26cdb75
Showing
1 changed file
with
177 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,177 @@ | ||
# usl-state | ||
[![For hire](/hireBadge.svg)](https://www.linkedin.com/in/asmyshlyaev177/) | ||
|
||
# urlstate | ||
|
||
![logo](/assets/logo.gif) | ||
|
||
[![npm](https://img.shields.io/npm/v/urlstate.svg)](https://www.npmjs.com/package/urlstate) | ||
![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/urlstate.svg) | ||
|
||
When working with Next.js client components sometimes you need to pass values between them, using `useSearchParams` is a good solution, but this library can help you to serialize complex objects and will preserve types. It is more convinient and types-safe solution for state management. | ||
|
||
## useUrlState hook | ||
|
||
`useUrlState` is a custom React hook for Next.js applications that manages state in the URL query string. It allows you to store and retrieve state from the URL search parameters, providing a way to persist state across page reloads and share application state via URLs. | ||
|
||
|
||
### Usage examples | ||
|
||
#### Basic | ||
|
||
```typescript | ||
'use client' | ||
import { useUrlState } from 'urlstate'; | ||
|
||
// State shape should be stored in a constant, don't pass an object directly | ||
const countState = { count: 0 }; | ||
|
||
function MyComponent() { | ||
const { state, updateState, updateUrl } = useUrlState(countState); | ||
|
||
// won't let you to accidently mutate state directly, requires TS | ||
// state.count = 2 // <- error | ||
|
||
return ( | ||
<div> | ||
<p>Count: {state.count}</p> | ||
<button onClick={() => updateUrl({ count: state.count + 1 })}> | ||
Increment (Update URL) | ||
</button> | ||
|
||
<button onClick={() => updateState({ count: state.count + 1 })}> | ||
Increment (Local Only) | ||
</button> | ||
<button onClick={() => updateUrl(state)}> | ||
Sync changes to url | ||
</button> | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
#### With complex state shape | ||
|
||
```typescript | ||
'use client' | ||
import { useUrlState } from 'urlstate'; | ||
|
||
interface UserSettings { | ||
theme: 'light' | 'dark'; | ||
fontSize: number; | ||
notifications: boolean; | ||
} | ||
|
||
const defaultSettings: UserSettings { | ||
theme: 'light', | ||
fontSize: 16, | ||
notifications: true, | ||
} | ||
|
||
function SettingsComponent() { | ||
// `state` will infer from UserSettings type! | ||
const { state, updateUrl } = useUrlState(defaultSettings); | ||
|
||
const toggleTheme = () => { | ||
updateUrl(current => ({ | ||
...current, | ||
theme: current.theme === 'light' ? 'dark' : 'light', | ||
})); | ||
}; | ||
|
||
return ( | ||
<div> | ||
<h2>User Settings</h2> | ||
<p>Theme: {state.theme}</p> | ||
<p>Font Size: {state.fontSize}px</p> | ||
<button onClick={toggleTheme}>Toggle Theme</button> | ||
{/* Other UI elements to update other settings */} | ||
</div> | ||
); | ||
} | ||
... | ||
|
||
// Other component | ||
function Component() { | ||
const { state } = useUrlState(defaultSettings); | ||
|
||
return ( | ||
<div> | ||
<p>Notifications is {state.notifications ? 'On' : 'Off'}</p> | ||
</div> | ||
) | ||
} | ||
``` | ||
|
||
#### With arbitrary state shape (not recommended) | ||
|
||
```typescript | ||
'use client' | ||
import { useUrlState } from 'urlstate'; | ||
|
||
const someObj = {}; | ||
|
||
function SettingsComponent() { | ||
const { state, updateUrl, updateState } = useUrlState<object>(someObj); | ||
} | ||
``` | ||
|
||
## useUrlEncode hook | ||
|
||
`useUrlEncode` is a custom React hook that provides utility functions for encoding and decoding state to and from URL search parameters. This hook doesn't depend on Nextjs, and will works with any React application. | ||
|
||
Accepts optional defaultState argument. | ||
|
||
```typescript | ||
import { useUrlEncode } from 'urlstate'; | ||
|
||
const Component = () => { | ||
const { parse, stringify } = useUrlEncode(); | ||
|
||
const str = stringify({ age: 36 }); // age=∓36 | ||
const obj = parse(str); // { age: 36 } | ||
|
||
const currentParams = parse(window.location.search); | ||
// OR | ||
// const obj = parse(new URLSearchParams(window.location.search)) | ||
|
||
const updateSearch = () => { | ||
const currentParams = new URLSearchParams(window.location.search); | ||
const newState = { query: 'react hooks', page: 2 }; | ||
const updatedParamsString = stringify(newState, currentParams); | ||
console.log(updatedParamsString); | ||
// Output: existing params + query=react%20hooks&page=2 | ||
}; | ||
} | ||
``` | ||
|
||
## `encode` and `decode` helpers | ||
|
||
There low level helpers to stringify and parse query string params. Useful for other frameworks or pure JS. | ||
|
||
```javascript | ||
import { encode, decode } from 'urlstate'; | ||
|
||
const state = encode({ obj: [1, 2, 3] }); | ||
|
||
// to params | ||
const params = new URLSearchParams(); | ||
Object.entries(state).forEach(([key, value]) => { | ||
params.set(key, encode(value)); | ||
}); | ||
|
||
// from params | ||
const obj = Object.fromEntries( | ||
[...params.entries()].map(([key, value]) => [ | ||
key, | ||
decode(value), | ||
]), | ||
) | ||
``` | ||
|
||
## Gothas | ||
|
||
1. Can pass only serializable values, `Function`, `BigInt` or `Symbol` won't work, probably things like `ArrayBuffer` neither. But won't throw an error if you do it accidently. | ||
2. Developed and tested with recent `Next.js` and `Typescript` versions. | ||
|
||
|
||
## [Changelog](https://github.com/asmyshlyaev177/urlstate/blob/main/CHANGELOG.md) |