state-in-url 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.CN.md CHANGED
@@ -25,6 +25,7 @@
25
25
  ![演示-gif](https://github.com/asmyshlyaev177/state-in-url/assets/19854148/c9802601-4d42-4362-b3e4-37ff87c3b97f)
26
26
 
27
27
  <a href="https://state-in-url-asmyshlyaev177.vercel.app/" target="_blank">演示</a> |
28
+ <a href="https://state-in-url.netlify.app/" target="_blank">DEMO2</a> |
28
29
  <a href ="https://codesandbox.io/p/github/asmyshlyaev177/state-in-url/main?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clyk5bd9y00062v6jspcfrkx7%2522%252C%2522sizes%2522%253A%255B100%252C0%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clyk5bd9x00022v6jyg71cr9e%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clyk5bd9x00042v6jsos2y043%2522%257D%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clyk5bd9x00052v6j5r632b12%2522%257D%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clyk5bd9x00022v6jyg71cr9e%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clyk5bd9w00012v6j83rq3bvo%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522clyk5bd9x00022v6jyg71cr9e%2522%252C%2522activeTabId%2522%253A%2522clyk5bd9w00012v6j83rq3bvo%2522%257D%252C%2522clyk5bd9x00052v6j5r632b12%2522%253A%257B%2522id%2522%253A%2522clyk5bd9x00052v6j5r632b12%2522%252C%2522activeTabId%2522%253A%2522clyk5bdjx000b2v6jfc8ae464%2522%252C%2522tabs%2522%253A%255B%257B%2522type%2522%253A%2522SETUP_TASKS%2522%252C%2522id%2522%253A%2522clyk5bdjx000b2v6jfc8ae464%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A2222%252C%2522id%2522%253A%2522clyk5cjbo004d2v6j3u55k74g%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522ENV_SETUP%2522%252C%2522id%2522%253A%2522clyk5h8dp000r2v6j0r7kc7qq%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%252C%2522clyk5bd9x00042v6jsos2y043%2522%253A%257B%2522id%2522%253A%2522clyk5bd9x00042v6jsos2y043%2522%252C%2522activeTabId%2522%253A%2522clyk5cbuv001q2v6joegrxxv6%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clyk5bd9x00032v6jtfhj316o%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522NEW_TERMINAL%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522id%2522%253A%2522clyk5cbuv001q2v6joegrxxv6%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522cleanupDist%2522%252C%2522id%2522%253A%2522clyk5dgvo005i2v6jj950sxft%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D" target="_blank">Demo on Codesandbox</a>
29
30
 
30
31
  <hr />
package/README.KO.md CHANGED
@@ -29,6 +29,7 @@
29
29
 
30
30
 
31
31
  <a href="https://state-in-url-asmyshlyaev177.vercel.app/" target="_blank">데모</a> |
32
+ <a href="https://state-in-url.netlify.app/" target="_blank">DEMO2</a> |
32
33
  <a href ="https://codesandbox.io/p/github/asmyshlyaev177/state-in-url/main?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clyk5bd9y00062v6jspcfrkx7%2522%252C%2522sizes%2522%253A%255B100%252C0%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clyk5bd9x00022v6jyg71cr9e%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clyk5bd9x00042v6jsos2y043%2522%257D%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clyk5bd9x00052v6j5r632b12%2522%257D%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clyk5bd9x00022v6jyg71cr9e%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clyk5bd9w00012v6j83rq3bvo%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522clyk5bd9x00022v6jyg71cr9e%2522%252C%2522activeTabId%2522%253A%2522clyk5bd9w00012v6j83rq3bvo%2522%257D%252C%2522clyk5bd9x00052v6j5r632b12%2522%253A%257B%2522id%2522%253A%2522clyk5bd9x00052v6j5r632b12%2522%252C%2522activeTabId%2522%253A%2522clyk5bdjx000b2v6jfc8ae464%2522%252C%2522tabs%2522%253A%255B%257B%2522type%2522%253A%2522SETUP_TASKS%2522%252C%2522id%2522%253A%2522clyk5bdjx000b2v6jfc8ae464%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A2222%252C%2522id%2522%253A%2522clyk5cjbo004d2v6j3u55k74g%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522ENV_SETUP%2522%252C%2522id%2522%253A%2522clyk5h8dp000r2v6j0r7kc7qq%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%252C%2522clyk5bd9x00042v6jsos2y043%2522%253A%257B%2522id%2522%253A%2522clyk5bd9x00042v6jsos2y043%2522%252C%2522activeTabId%2522%253A%2522clyk5cbuv001q2v6joegrxxv6%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clyk5bd9x00032v6jtfhj316o%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522NEW_TERMINAL%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522id%2522%253A%2522clyk5cbuv001q2v6joegrxxv6%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522cleanupDist%2522%252C%2522id%2522%253A%2522clyk5dgvo005i2v6jj950sxft%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D" target="_blank">Demo on Codesandbox</a>
33
34
 
34
35
 
package/README.md CHANGED
@@ -2,18 +2,15 @@ English | [한국어](./README.KO.md) | [简体中文](./README.CN.md)
2
2
 
3
3
  <h1 align="center">state-in-url</h1>
4
4
 
5
-
6
5
  <div align="center">
7
6
  <img src="/assets/logo.svg?raw=true" alt="Library logo"/>
8
7
 
9
8
  <div>Easily share state between unrelated React components, with IDE autocomplete and TS validation. Without any hasssle or boilerplate. </div>
10
9
  </div>
11
10
 
12
-
13
11
  <div align="center">
14
12
  <hr />
15
13
 
16
-
17
14
  [![npm](https://img.shields.io/npm/v/state-in-url.svg)](https://www.npmjs.com/package/state-in-url)
18
15
  ![Tests](https://github.com/asmyshlyaev177/state-in-url/actions/workflows/tests.yml/badge.svg?branch=main)
19
16
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/73be54068b7f41b0b74a252579ac09ec)](https://app.codacy.com/gh/asmyshlyaev177/state-in-url/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
@@ -27,11 +24,10 @@ English | [한국어](./README.KO.md) | [简体中文](./README.CN.md)
27
24
 
28
25
  ![Demo-gif](https://github.com/asmyshlyaev177/state-in-url/assets/19854148/c9802601-4d42-4362-b3e4-37ff87c3b97f)
29
26
 
30
-
31
27
  <a href="https://state-in-url-asmyshlyaev177.vercel.app/" target="_blank">DEMO</a> |
28
+ <a href="https://state-in-url.netlify.app/" target="_blank">DEMO2</a> |
32
29
  <a href ="https://codesandbox.io/p/github/asmyshlyaev177/state-in-url/main?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clyk5bd9y00062v6jspcfrkx7%2522%252C%2522sizes%2522%253A%255B100%252C0%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clyk5bd9x00022v6jyg71cr9e%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clyk5bd9x00042v6jsos2y043%2522%257D%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clyk5bd9x00052v6j5r632b12%2522%257D%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clyk5bd9x00022v6jyg71cr9e%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clyk5bd9w00012v6j83rq3bvo%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522clyk5bd9x00022v6jyg71cr9e%2522%252C%2522activeTabId%2522%253A%2522clyk5bd9w00012v6j83rq3bvo%2522%257D%252C%2522clyk5bd9x00052v6j5r632b12%2522%253A%257B%2522id%2522%253A%2522clyk5bd9x00052v6j5r632b12%2522%252C%2522activeTabId%2522%253A%2522clyk5bdjx000b2v6jfc8ae464%2522%252C%2522tabs%2522%253A%255B%257B%2522type%2522%253A%2522SETUP_TASKS%2522%252C%2522id%2522%253A%2522clyk5bdjx000b2v6jfc8ae464%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A2222%252C%2522id%2522%253A%2522clyk5cjbo004d2v6j3u55k74g%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522ENV_SETUP%2522%252C%2522id%2522%253A%2522clyk5h8dp000r2v6j0r7kc7qq%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%252C%2522clyk5bd9x00042v6jsos2y043%2522%253A%257B%2522id%2522%253A%2522clyk5bd9x00042v6jsos2y043%2522%252C%2522activeTabId%2522%253A%2522clyk5cbuv001q2v6joegrxxv6%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clyk5bd9x00032v6jtfhj316o%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522NEW_TERMINAL%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522id%2522%253A%2522clyk5cbuv001q2v6joegrxxv6%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522cleanupDist%2522%252C%2522id%2522%253A%2522clyk5dgvo005i2v6jj950sxft%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D" target="_blank">Demo on Codesandbox</a>
33
30
 
34
-
35
31
  <hr />
36
32
 
37
33
  Add a ⭐️ to support the project!
@@ -40,11 +36,10 @@ Add a ⭐️ to support the project!
40
36
 
41
37
  </div>
42
38
 
43
- ## Why use state-in-url?
39
+ ## Why use state-in-url?
44
40
 
45
41
  `state-in-url` Simple state management with optional URL sync. Share complex states between unrelated React components, TS-friendly, NextJS compatible.
46
42
 
47
-
48
43
  # Use cases
49
44
 
50
45
  - 🧮 Store unsaved user forms in URL
@@ -53,8 +48,6 @@ Add a ⭐️ to support the project!
53
48
  - 🔗 Shareable URLs with full application state
54
49
  - 🔄 Easy state persistence across page reloads
55
50
 
56
-
57
-
58
51
  # Features
59
52
 
60
53
  - 🧩 **Simple**: No providers, reducers, boilerplate or new concepts
@@ -64,7 +57,6 @@ Add a ⭐️ to support the project!
64
57
  - ⚡ **Fast**: Minimal rerenders
65
58
  - 🪶 **Lightweight**: Zero dependencies for a smaller footprint
66
59
 
67
-
68
60
  ## Table of content
69
61
 
70
62
  - [Installation](#installation)
@@ -129,8 +121,10 @@ import { countState } from './countState';
129
121
  function MyComponent() {
130
122
  // for use searchParams from server component
131
123
  // e.g. export default async function Home({ searchParams }: { searchParams: object }) {
132
- // const { state, updateState, updateUrl } = useUrlState(countState, searchParams);
133
- const { state, updateState, updateUrl } = useUrlState(countState);
124
+ // const { state, updateState, updateUrl } = useUrlState({ defaultState: countState, searchParams });
125
+ // can pass `replace` arg, it's control will `updateUrl` will use `rounter.push` or `router.replace`, default replace=true
126
+ // const { state, updateState, updateUrl } = useUrlState({ defaultState: countState, searchParams, replace: false });
127
+ const { state, updateState, updateUrl } = useUrlState({ defaultState: countState });
134
128
 
135
129
  // won't let you to accidently mutate state directly, requires TS
136
130
  // state.count = 2 // <- error
@@ -184,7 +178,7 @@ import { userSettings } from './userSettings';
184
178
 
185
179
  function SettingsComponent() {
186
180
  // `state` will infer from UserSettings type!
187
- const { state, updateUrl } = useUrlState(userSettings);
181
+ const { state, updateUrl } = useUrlState({ defaultState: userSettings });
188
182
 
189
183
  const toggleTheme = () => {
190
184
  updateUrl(current => ({
@@ -221,7 +215,7 @@ function SettingsComponent() {
221
215
 
222
216
  // Other component
223
217
  function Component() {
224
- const { state } = useUrlState(defaultSettings);
218
+ const { state } = useUrlState({ defaultState: defaultSettings });
225
219
 
226
220
  return (
227
221
  <div>
@@ -248,12 +242,18 @@ function Component() {
248
242
  }, [state, updateUrl]);
249
243
  ```
250
244
 
245
+ Syncing state `onBlur` will be more aligned with real world usage.
246
+
247
+ ```typescript
248
+ <input onBlur={() => updateUrl()} .../>
249
+ ```
250
+
251
251
  #### With server side rendering
252
252
 
253
253
  ```typescript
254
254
  export default async function Home({ searchParams }: { searchParams: object }) {
255
255
  return (
256
- <Form sp={searchParams} />
256
+ <Form searchParams={searchParams} />
257
257
  )
258
258
  }
259
259
 
@@ -263,8 +263,8 @@ import React from 'react';
263
263
  import { useUrlState } from 'state-in-url/next';
264
264
  import { form } from './form';
265
265
 
266
- const Form = ({ sp }: { sp: object }) => {
267
- const { state, updateState, updateUrl } = useUrlState(form, sp);
266
+ const Form = ({ searchParams }: { searchParams: object }) => {
267
+ const { state, updateState, updateUrl } = useUrlState({ defaultState: form, searchParams });
268
268
  }
269
269
  ```
270
270
 
@@ -329,6 +329,7 @@ function SettingsComponent() {
329
329
  ```
330
330
 
331
331
  ## `useSharedState` hook for React.js
332
+
332
333
  Hook to share state between any React components, tested with Next.js and Vite.
333
334
 
334
335
  ```typescript
@@ -368,7 +369,7 @@ function SettingsComponent() {
368
369
  ## Gothas
369
370
 
370
371
  1. Can pass only serializable values, `Function`, `BigInt` or `Symbol` won't work, probably things like `ArrayBuffer` neither.
371
- 2. Vercel servers limit size of headers (query string and other stuff) to **14KB**, so keep your URL state under ~5000 words. https://vercel.com/docs/errors/URL_TOO_LONG
372
+ 2. Vercel servers limit size of headers (query string and other stuff) to **14KB**, so keep your URL state under ~5000 words. <https://vercel.com/docs/errors/URL_TOO_LONG>
372
373
  3. Tested with `next.js` 14 with app router, no plans to support pages.
373
374
 
374
375
  ## Run locally
@@ -381,7 +382,6 @@ npm run dev
381
382
 
382
383
  Go to [localhost:3000](http://localhost:3000)
383
384
 
384
-
385
385
  ## Contact & Support
386
386
 
387
387
  - Create a [GitHub issue](https://github.com/asmyshlyaev177/state-in-url/issues) for bug reports, feature requests, or questions
@@ -394,7 +394,7 @@ This project is licensed under the [MIT license](https://github.com/asmyshlyaev1
394
394
 
395
395
  ## Inspiration
396
396
 
397
- [Using URL to store state in Vue ](https://dev.to/jacobandrewsky/using-url-to-store-state-in-vue-275c)
397
+ [Using URL to store state in Vue](https://dev.to/jacobandrewsky/using-url-to-store-state-in-vue-275c)
398
398
 
399
399
  [Storing state in the URL](https://antonz.org/storing-state/)
400
400
 
@@ -1,6 +1,7 @@
1
- import { useRouter } from 'next/navigation';
2
1
  import { type DeepReadonly, type JSONCompatible } from '../../utils';
3
2
  /**
3
+ * @deprecated Pass arguments in a object `useUrlState({ defaultState: form, searchParams })`
4
+ *
4
5
  * NextJS hook. Returns `state`, `updateState`, and `updateUrl` functions
5
6
  *
6
7
  * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
@@ -23,34 +24,40 @@ import { type DeepReadonly, type JSONCompatible } from '../../utils';
23
24
  * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#api}
24
25
  */
25
26
  export declare function useUrlState<T extends JSONCompatible>(defaultState: T, searchParams?: object): {
26
- /**
27
- * * Example:
28
- * ```ts
29
- * updateState({ name: 'test' });
30
- * // or
31
- * updateState(curr => ({ ...curr, name: 'test' }) );
32
- * ```
33
- *
34
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#updatestate}
35
- */
27
+ state: DeepReadonly<T>;
36
28
  updateState: (value: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
37
- /**
38
- * * Example:
39
- * ```ts
40
- * updateUrl({ name: 'test' });
41
- * // or
42
- * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: false } );
43
- * ```
44
- *
45
- * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#updateurl}
46
- */
47
- updateUrl: (value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T) | undefined, options?: Options) => void;
48
- state: DeepReadonly<DeepReadonly<T>>;
49
- getState: () => T;
29
+ updateUrl: (value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
30
+ getState: () => DeepReadonly<T>;
50
31
  };
51
- type Router = ReturnType<typeof useRouter>;
52
- type RouterOptions = NonNullable<Parameters<Router['push']>[1] | Parameters<Router['replace']>[1]>;
53
- interface Options extends RouterOptions {
32
+ /**
33
+ * NextJS hook. Returns `state`, `updateState`, and `updateUrl` functions
34
+ *
35
+ * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
36
+ * @param {?SearchParams<T>} [searchParams] searchParams from Next server component
37
+ *
38
+ * * Example:
39
+ * ```ts
40
+ * export const form = { name: '', age: 0 };
41
+ * const { state, updateState, updateUrl } = useUrlState({ defaultState: form });
42
+ * // for nextjs seerver components
43
+ * // const { state, updateState, updateUrl } = useUrlState({ defaultState: form, searchParams });
44
+ *
45
+ * updateState({ name: 'test' });
46
+ * // by default it's uses router.push with scroll: false
47
+ * updateUrl({ name: 'test' }, { replace: true, scroll: true });
48
+ * // similar to React.useState
49
+ * updateUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
50
+ * ```
51
+ *
52
+ * * Github {@link https://github.com/asmyshlyaev177/state-in-url/tree/main/packages/urlstate/next/useUrlState#api}
53
+ */
54
+ export declare function useUrlState<T extends JSONCompatible>({ defaultState, searchParams, }: {
55
+ defaultState: T;
56
+ searchParams?: object;
54
57
  replace?: boolean;
55
- }
56
- export {};
58
+ }): {
59
+ state: DeepReadonly<T>;
60
+ updateState: (value: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
61
+ updateUrl: (value?: Partial<T> | Partial<DeepReadonly<T>> | ((currState: T) => T)) => void;
62
+ getState: () => DeepReadonly<T>;
63
+ };
@@ -1 +1 @@
1
- import{useRouter as t,useSearchParams as e}from"next/navigation";import r from"react";import{parseSsrQs as a}from"../parseSsrQs.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";function o(o,i){const m=t(),{state:n,updateState:u,updateUrl:c,getState:l}=s(o,m,i),p=r.useCallback(((t,e)=>{c(t,{scroll:!1,...e})}),[c]),f=e();return r.useEffect((()=>{const t=Object.keys(o),e=Object.fromEntries([...f.entries()].filter((([e])=>t.includes(e))));u(a(e,o))}),[f]),{updateState:u,updateUrl:p,state:n,getState:l}}export{o as useUrlState};
1
+ import{useRouter as t,useSearchParams as e}from"next/navigation";import a from"react";import{parseSsrQs as r}from"../parseSsrQs.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";function c(c,l){const{_defaultState:n,_searchParams:u,_replace:o}=function(t,e){const a="defaultState"in t?t.defaultState:t,r="defaultState"in t?t.searchParams:e,s="defaultState"in t&&(t.replace??!0);return{_defaultState:a,_searchParams:r,_replace:s}}(c,l),f=t(),{state:i,updateState:m,updateUrl:p,getState:S}=s(n,f,u),d=a.useCallback(((t,e)=>{const a={scroll:!1,replace:o,...e};p(t,a)}),[p,o]),_=e();return a.useEffect((()=>{const t=Object.keys(n),e=Object.fromEntries([..._.entries()].filter((([e])=>t.includes(e))));m(r(e,n))}),[_]),{updateState:m,updateUrl:d,state:i,getState:S}}export{c as useUrlState};
@@ -1 +1 @@
1
- import t from"react";import{parseSsrQs as e}from"../next/parseSsrQs.mjs";import{useInsertionEffect as n}from"../useInsertionEffect.mjs";import{useSharedState as o}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as r}from"../useUrlEncode/useUrlEncode.mjs";import{isSSR as s}from"../utils.mjs";function a(a,i,m){const{parse:w,stringify:u}=r(a),{state:l,getState:d,setState:f}=o(a,(()=>s()?e(function(t,e){const n=Object.keys(t),o=Object.fromEntries(Object.entries(e||{}).filter((([t])=>n.includes(t))));return o}(a,m),a):w(function(t){const e=new URLSearchParams(window.location.search),n=Object.keys(t),o=new URLSearchParams;return[...e.entries()].filter((([t])=>n.includes(t))).forEach((([t,e])=>o.set(t,e))),o.toString()}(a))));n((()=>{const t=()=>{const t=w(window.location.search);f(t)};return window.addEventListener(c,t),()=>{window.removeEventListener(c,t)}}),[f]);const h=t.useCallback(((t,e)=>{const n=`${window.location.pathname}${window.location.search}${window.location.hash}`,o="function"==typeof t,r=function(t){const e=Object.keys(t),n=window.location.search,o=new URLSearchParams(n),r=new URLSearchParams;return o.forEach(((t,n)=>!e.includes(n)&&r.set(n,t))),r}(a),s=o?t(d()):t?{...d(),...t}:d(),c=u(s,r);f(s);const m=`${window.location.pathname}${c.length?"?":""}${c}${window.location.hash}`;if(n!==m){const{replace:t,...n}=e||{};i[t?"replace":"push"](m,{...n})}}),[i,u,d]);return{updateState:f,updateUrl:h,state:l,getState:d}}const c="popstate";export{a as useUrlStateBase};
1
+ import t from"react";import{parseSsrQs as e}from"../next/parseSsrQs.mjs";import{useInsertionEffect as n}from"../useInsertionEffect.mjs";import{useSharedState as o}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as r}from"../useUrlEncode/useUrlEncode.mjs";import{isSSR as s}from"../utils.mjs";function a(a,m,u){const{parse:w,stringify:f}=r(a),{state:l,getState:d,setState:h}=o(a,(()=>s()?e(function(t,e){const n=Object.keys(t),o=Object.fromEntries(Object.entries(e||{}).filter((([t])=>n.includes(t))));return o}(a,u),a):w(c(a))));n((()=>{const t=()=>{const t=w(c(a));h(t)};return window.addEventListener(i,t),()=>{window.removeEventListener(i,t)}}),[h]);const p=t.useCallback(((t,e)=>{const n=`${window.location.pathname}${window.location.search}${window.location.hash}`,o="function"==typeof t,r=function(t){const e=Object.keys(t),n=window.location.search,o=new URLSearchParams(n),r=new URLSearchParams;return o.forEach(((t,n)=>!e.includes(n)&&r.set(n,t))),r}(a),s=o?t(d()):t?{...d(),...t}:d(),c=f(s,r);h(s);const i=`${window.location.pathname}${c.length?"?":""}${c}${window.location.hash}`;if(n!==i){const{replace:t,...n}=e||{};m[t?"replace":"push"](i,{...n})}}),[m,f,d]);return{updateState:h,updateUrl:p,state:l,getState:d}}function c(t){const e=new URLSearchParams(window.location.search),n=Object.keys(t),o=new URLSearchParams;return[...e.entries()].filter((([t])=>n.includes(t))).forEach((([t,e])=>o.set(t,e))),o.toString()}const i="popstate";export{a as useUrlStateBase};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Library to store state in browser URL, includes hook for NextJS, hook for pure React, and low level helpers.",
5
5
  "homepage": "https://github.com/asmyshlyaev177/state-in-url#readme",
6
6
  "keywords": [
@@ -120,7 +120,7 @@
120
120
  ]
121
121
  },
122
122
  "test:int": {
123
- "command": "playwright test -j 3 --project=chromium --retries=2",
123
+ "command": "playwright test --project=chromium --retries=2",
124
124
  "dependencies": [
125
125
  "example:react"
126
126
  ]
@@ -229,8 +229,8 @@
229
229
  "@rollup/plugin-node-resolve": "^15.2.3",
230
230
  "@rollup/plugin-terser": "^0.4.4",
231
231
  "@rollup/plugin-typescript": "^11.1.6",
232
- "@testing-library/dom": "^10.2.0",
233
- "@testing-library/jest-dom": "^6.4.2",
232
+ "@testing-library/dom": "^10.4.0",
233
+ "@testing-library/jest-dom": "^6.4.8",
234
234
  "@testing-library/react": "^16.0.0",
235
235
  "@testing-library/react-hooks": "^8.0.1",
236
236
  "@types/glob": "^8.1.0",