state-in-url 0.2.0 → 0.2.2
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.md +28 -5
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +1 -0
- package/dist/urlstate/state.d.ts +3 -3
- package/dist/urlstate/useUrlEncode.d.ts +2 -2
- package/dist/urlstate/useUrlState.d.ts +3 -3
- package/dist/urlstate/utils.d.ts +3 -3
- package/package.json +14 -7
- package/dist/index.js +0 -1
package/README.md
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
<h1 align="center">state-in-url</h1>
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
<
|
|
5
|
+
<div align="center">
|
|
6
6
|
<img src="/assets/logo.svg?raw=true" alt="Library logo"/>
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
<div>Seamlessly store complex nested state object in URL query parameters with IDE autotomplete and TS validation.</div>
|
|
9
|
+
</div>
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
<div align="center">
|
|
13
|
+
<hr />
|
|
11
14
|
|
|
12
15
|
[](https://www.linkedin.com/in/asmyshlyaev177/)
|
|
13
16
|
|
|
@@ -19,7 +22,6 @@
|
|
|
19
22
|
</div>
|
|
20
23
|
|
|
21
24
|
<div align="center">
|
|
22
|
-
Seamlessly store complex React state object in URL query parameters without losing types. Works with Next.js/React.js applications.
|
|
23
25
|
|
|
24
26
|
<h4 align="center">Don't hesitate to open issue if you found a bug</h4>
|
|
25
27
|
|
|
@@ -46,12 +48,14 @@ Add a ⭐️ to support the project!
|
|
|
46
48
|
- 🔗 Shareable URLs with full application state
|
|
47
49
|
- 🔄 Easy state persistence across page reloads
|
|
48
50
|
- 🧠 Pass data between unrelated client components
|
|
51
|
+
- 🙃 Can just share the state without changing url
|
|
49
52
|
- 🧮 Store unsaved user forms in URL
|
|
50
53
|
|
|
51
54
|
# Features
|
|
52
55
|
- 🧩 **Simple**: Handles complex objects without extra effort
|
|
53
|
-
- 📘 **Typescript support and type Safety**: Preserves data types and structure, enhances developer experience with strong typing and JSDoc comments
|
|
56
|
+
- 📘 **Typescript support and type Safety**: Preserves data types and structure, enhances developer experience with IDE suggestions, strong typing and JSDoc comments
|
|
54
57
|
- ⚛️ **Framework Flexibility**: Separate hooks for Next.js and React.js applications, and functions for pure JS
|
|
58
|
+
- ⚙ **Well tested**: Unit tests and Playwright tests
|
|
55
59
|
- 🪶 **Lightweight**: Zero dependencies for a smaller footprint
|
|
56
60
|
|
|
57
61
|
|
|
@@ -70,6 +74,7 @@ Add a ⭐️ to support the project!
|
|
|
70
74
|
- [Inspiration](#inspiration)
|
|
71
75
|
|
|
72
76
|
## installation
|
|
77
|
+
### 1. Install package
|
|
73
78
|
```sh
|
|
74
79
|
# npm
|
|
75
80
|
npm install --save state-in-url
|
|
@@ -78,6 +83,8 @@ yarn add state-in-url
|
|
|
78
83
|
# pnpm
|
|
79
84
|
pnpm add state-in-url
|
|
80
85
|
```
|
|
86
|
+
### 2. Edit tsconfig.json
|
|
87
|
+
set `"moduleResolution": "Node16"` or `"moduleResolution": "NodeNext"` in your `tsconfig.json`
|
|
81
88
|
|
|
82
89
|
|
|
83
90
|
## useUrlState hook for Next.js
|
|
@@ -113,11 +120,13 @@ function MyComponent() {
|
|
|
113
120
|
Increment (Update URL)
|
|
114
121
|
</button>
|
|
115
122
|
|
|
116
|
-
|
|
123
|
+
// same api as React.useState
|
|
124
|
+
<button onClick={() => updateState(currState => ({...currState, count: currState.count + 1 }) )}>
|
|
117
125
|
Increment (Local Only)
|
|
118
126
|
</button>
|
|
119
127
|
<button onClick={() => updateUrl()}>
|
|
120
128
|
Sync changes to url
|
|
129
|
+
// Or don't sync it and just share state
|
|
121
130
|
</button>
|
|
122
131
|
|
|
123
132
|
<button onClick={() => updateUrl(state)}>
|
|
@@ -157,6 +166,20 @@ function SettingsComponent() {
|
|
|
157
166
|
}));
|
|
158
167
|
};
|
|
159
168
|
|
|
169
|
+
// sync state to url when idle
|
|
170
|
+
const timer = React.useRef(0 as unknown as NodeJS.Timeout);
|
|
171
|
+
React.useEffect(() => {
|
|
172
|
+
clearTimeout(timer.current);
|
|
173
|
+
timer.current = setTimeout(() => {
|
|
174
|
+
// will compare state by content not by reference and fire update only for new values
|
|
175
|
+
updateUrl(state);
|
|
176
|
+
}, 500);
|
|
177
|
+
|
|
178
|
+
return () => {
|
|
179
|
+
clearTimeout(timer.current);
|
|
180
|
+
};
|
|
181
|
+
}, [state, updateUrl]);
|
|
182
|
+
|
|
160
183
|
return (
|
|
161
184
|
<div>
|
|
162
185
|
<h2>User Settings</h2>
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("next/navigation");function n(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}var r=n(e);const c=e=>{const t=null===e,n=Array.isArray(e),r=e instanceof Date;return(t?"null":r&&"date")||n&&"array"||!t&&!r&&!n&&"object"==typeof e&&"object"||typeof e},u=()=>"undefined"==typeof window,o=e=>new URLSearchParams("string"==typeof e?e:e?.toString?.()||""),a=(e,t)=>JSON.stringify(e)===JSON.stringify(t),s={string:"◖",boolean:"🗵",null:"∙null",undefined:"∙undefined",number:"∓",date:"⏲"};function i(e){switch(c(e)){case"function":case"symbol":return"";case"date":return s.date+e.toISOString();case"string":return e.match(/^◖/)?encodeURIComponent(e):`${s.string}${encodeURIComponent(e)}`;case"number":return s.number+String(e);case"boolean":return s.boolean+String(e);case"object":case"array":return JSON.stringify(e,f).replaceAll('"',"'");case"null":return s.null;case"undefined":return s.undefined;default:return String(e)}}function l(e,t){return function(e,t){try{return JSON.parse(e,g)}catch{const n=d(e);return n!==p?n:t}}(e.replaceAll("'",'"'),t)}const f=(e,t)=>{const n=c(t);return e&&"object"!==n&&"array"!==n?i(t):t},d=e=>e===s.null?null:e!==s.undefined?e?.startsWith?.(s.number)?Number.parseFloat(e.replace(s.number,"")):e?.startsWith?.(s.boolean)?!!e.includes("true"):e?.startsWith?.(s.date)?new Date(e.slice(1)):e?.startsWith?.(s.string)?decodeURIComponent(e).replace(/^◖/,""):p:void 0,p=Symbol("isError"),g=(e,t)=>{const n="string"==typeof t,r=n&&d(t);return r===p?t:e&&n?r:t};const y=(e,t)=>{const n="string"==typeof t,r=n&&l(t?.replaceAll?.("'",'"'));return r===p?t:e&&n?r:t};function b(e,t){try{return JSON.parse(e,y)}catch{const n=d(e);return n!==p?n:t}}function S(e,t,n){const r=o(n);return Object.entries(e||{}).forEach((([e,n])=>{const c=t?.[e];JSON.stringify(n)!==JSON.stringify(c)&&r.set(e,i(n))})),r.toString()}function h(e,t){return{...t||{},...Object.fromEntries([...o(e).entries()].map((([e,n])=>{const r=t?.[e];return[e,l(n,r)??r]})))}}function m(e){const t=r.default.useCallback((function(t,n){return"object"===c(t)?S(t,e,n):""}),[e]);return{parse:r.default.useCallback((function(t){return h(t,e)}),[e]),stringify:t}}const w=new WeakMap,O=new WeakMap;exports.decode=l,exports.decodeState=h,exports.encode=i,exports.encodeState=S,exports.useUrlEncode=m,exports.useUrlState=function(e,n){const{state:c,getState:o,setState:s,stringify:i}=function(e){const t=r.default.useRef(e),{parse:n,stringify:c}=m(t.current),[o,s]=r.default.useState((()=>{const e=w.get(t.current),r=n(u()?"":window.location.search);return e||w.set(t.current,r),r}));return r.default.useInsertionEffect((()=>{const n=O.get(t.current)||[],r=()=>{s(w.get(t.current)||e)};return O.set(t.current,n.concat(r)),()=>{const e=O.get(t.current)||[];O.set(t.current,e.filter((e=>e!==r)))}}),[]),{state:o,getState:r.default.useCallback((()=>w.get(t.current)||t.current),[]),setState:r.default.useCallback((e=>{const n=w.get(t.current)||t.current;if("function"==typeof e){const r=e(n);if(a(n,r))return!1;w.set(t.current,r),(O.get(t.current)||[]).forEach((e=>{e()}))}else{if(a(n,e))return!1;w.set(t.current,e),(O.get(t.current)||[]).forEach((e=>{e()}))}}),[]),parse:n,stringify:c}}(u()?function(e,t){return{...t,...b(JSON.stringify(e),t)}}(n,e):e),l=t.useRouter();return{updateUrl:r.default.useCallback(((e,t)=>{e&&s(e);const n=window.location.search,r=`${window.location.pathname}${n.length&&!n.includes("?")?"?":""}${n}`,c=i("function"==typeof e?e(o()):o()),u=`${window.location.pathname}${c.length?"?":""}${c}`;if(r!==u){const{replace:e,...n}=t||{};l[e?"replace":"push"](u,{scroll:!1,...n})}}),[l,i,o]),updateState:s,state:c}};
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"react";import{useRouter as e}from"next/navigation";const n=t=>{const e=null===t,n=Array.isArray(t),r=t instanceof Date;return(e?"null":r&&"date")||n&&"array"||!e&&!r&&!n&&"object"==typeof t&&"object"||typeof t},r=()=>"undefined"==typeof window,c=t=>new URLSearchParams("string"==typeof t?t:t?.toString?.()||""),o=(t,e)=>JSON.stringify(t)===JSON.stringify(e),s={string:"◖",boolean:"🗵",null:"∙null",undefined:"∙undefined",number:"∓",date:"⏲"};function a(t){switch(n(t)){case"function":case"symbol":return"";case"date":return s.date+t.toISOString();case"string":return t.match(/^◖/)?encodeURIComponent(t):`${s.string}${encodeURIComponent(t)}`;case"number":return s.number+String(t);case"boolean":return s.boolean+String(t);case"object":case"array":return JSON.stringify(t,i).replaceAll('"',"'");case"null":return s.null;case"undefined":return s.undefined;default:return String(t)}}function u(t,e){return function(t,e){try{return JSON.parse(t,g)}catch{const n=l(t);return n!==f?n:e}}(t.replaceAll("'",'"'),e)}const i=(t,e)=>{const r=n(e);return t&&"object"!==r&&"array"!==r?a(e):e},l=t=>t===s.null?null:t!==s.undefined?t?.startsWith?.(s.number)?Number.parseFloat(t.replace(s.number,"")):t?.startsWith?.(s.boolean)?!!t.includes("true"):t?.startsWith?.(s.date)?new Date(t.slice(1)):t?.startsWith?.(s.string)?decodeURIComponent(t).replace(/^◖/,""):f:void 0,f=Symbol("isError"),g=(t,e)=>{const n="string"==typeof e,r=n&&l(e);return r===f?e:t&&n?r:e};const p=(t,e)=>{const n="string"==typeof e,r=n&&u(e?.replaceAll?.("'",'"'));return r===f?e:t&&n?r:e};function d(t,e){try{return JSON.parse(t,p)}catch{const n=l(t);return n!==f?n:e}}function y(t,e,n){const r=c(n);return Object.entries(t||{}).forEach((([t,n])=>{const c=e?.[t];JSON.stringify(n)!==JSON.stringify(c)&&r.set(t,a(n))})),r.toString()}function b(t,e){return{...e||{},...Object.fromEntries([...c(t).entries()].map((([t,n])=>{const r=e?.[t];return[t,u(n,r)??r]})))}}function S(e){const r=t.useCallback((function(t,r){return"object"===n(t)?y(t,e,r):""}),[e]);return{parse:t.useCallback((function(t){return b(t,e)}),[e]),stringify:r}}const m=new WeakMap,h=new WeakMap;function w(n,c){const{state:s,getState:a,setState:u,stringify:i}=function(e){const n=t.useRef(e),{parse:c,stringify:s}=S(n.current),[a,u]=t.useState((()=>{const t=m.get(n.current),e=c(r()?"":window.location.search);return t||m.set(n.current,e),e}));return t.useInsertionEffect((()=>{const t=h.get(n.current)||[],r=()=>{u(m.get(n.current)||e)};return h.set(n.current,t.concat(r)),()=>{const t=h.get(n.current)||[];h.set(n.current,t.filter((t=>t!==r)))}}),[]),{state:a,getState:t.useCallback((()=>m.get(n.current)||n.current),[]),setState:t.useCallback((t=>{const e=m.get(n.current)||n.current;if("function"==typeof t){const r=t(e);if(o(e,r))return!1;m.set(n.current,r),(h.get(n.current)||[]).forEach((t=>{t()}))}else{if(o(e,t))return!1;m.set(n.current,t),(h.get(n.current)||[]).forEach((t=>{t()}))}}),[]),parse:c,stringify:s}}(r()?function(t,e){return{...e,...d(JSON.stringify(t),e)}}(c,n):n),l=e();return{updateUrl:t.useCallback(((t,e)=>{t&&u(t);const n=window.location.search,r=`${window.location.pathname}${n.length&&!n.includes("?")?"?":""}${n}`,c=i("function"==typeof t?t(a()):a()),o=`${window.location.pathname}${c.length?"?":""}${c}`;if(r!==o){const{replace:t,...n}=e||{};l[t?"replace":"push"](o,{scroll:!1,...n})}}),[l,i,a]),updateState:u,state:s}}export{u as decode,b as decodeState,a as encode,y as encodeState,S as useUrlEncode,w as useUrlState};
|
package/dist/urlstate/state.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type JSONCompatible, type DeepReadonly } from './utils';
|
|
|
2
2
|
export declare function useState<T>(defaultState: JSONCompatible<T>): {
|
|
3
3
|
state: JSONCompatible<T>;
|
|
4
4
|
getState: () => JSONCompatible<T>;
|
|
5
|
-
setState: (value: JSONCompatible<T> |
|
|
6
|
-
parse: (strOrSearchParams: string | URLSearchParams) =>
|
|
7
|
-
stringify: (state: JSONCompatible<T> |
|
|
5
|
+
setState: (value: JSONCompatible<T> | DeepReadonly<JSONCompatible<T>> | ((currState: typeof defaultState | DeepReadonly<typeof defaultState>) => typeof defaultState | DeepReadonly<typeof defaultState>)) => false | undefined;
|
|
6
|
+
parse: (strOrSearchParams: string | URLSearchParams) => DeepReadonly<JSONCompatible<T>>;
|
|
7
|
+
stringify: (state: JSONCompatible<T> | DeepReadonly<JSONCompatible<T>>, paramsToKeep?: string | URLSearchParams | undefined) => string;
|
|
8
8
|
};
|
|
@@ -20,6 +20,6 @@ import { type JSONCompatible, type DeepReadonly } from './utils';
|
|
|
20
20
|
* * Github {@link https://github.com/asmyshlyaev177/state-in-url}
|
|
21
21
|
*/
|
|
22
22
|
export declare function useUrlEncode<T>(stateShape: JSONCompatible<T>): {
|
|
23
|
-
parse: (strOrSearchParams: string | URLSearchParams) =>
|
|
24
|
-
stringify: (state: typeof stateShape |
|
|
23
|
+
parse: (strOrSearchParams: string | URLSearchParams) => DeepReadonly<JSONCompatible<T>>;
|
|
24
|
+
stringify: (state: typeof stateShape | DeepReadonly<typeof stateShape>, paramsToKeep?: string | URLSearchParams) => string;
|
|
25
25
|
};
|
|
@@ -19,9 +19,9 @@ import { DeepReadonly, type JSONCompatible } from './utils';
|
|
|
19
19
|
* * Github {@link https://github.com/asmyshlyaev177/state-in-url}
|
|
20
20
|
*/
|
|
21
21
|
export declare function useUrlState<T>(defaultState: JSONCompatible<T>, sp?: object): {
|
|
22
|
-
updateUrl: (value?:
|
|
23
|
-
updateState: (value: JSONCompatible<T> |
|
|
24
|
-
state: JSONCompatible<T
|
|
22
|
+
updateUrl: (value?: JSONCompatible<T> | DeepReadonly<JSONCompatible<T>> | ((currState: typeof defaultState | DeepReadonly<typeof defaultState>) => typeof defaultState | DeepReadonly<typeof defaultState>) | undefined, options?: Options) => void;
|
|
23
|
+
updateState: (value: JSONCompatible<T> | DeepReadonly<JSONCompatible<T>> | ((currState: JSONCompatible<T> | DeepReadonly<JSONCompatible<T>>) => JSONCompatible<T> | DeepReadonly<JSONCompatible<T>>)) => false | undefined;
|
|
24
|
+
state: DeepReadonly<JSONCompatible<T>>;
|
|
25
25
|
};
|
|
26
26
|
type Router = ReturnType<typeof useRouter>;
|
|
27
27
|
type RouterOptions = NonNullable<Parameters<Router['push']>[1]>;
|
package/dist/urlstate/utils.d.ts
CHANGED
|
@@ -17,9 +17,9 @@ type NotAssignableToJson = bigint | symbol | Function;
|
|
|
17
17
|
export type JSONCompatible<T> = unknown extends T ? never : {
|
|
18
18
|
[P in keyof T]: T[P] extends JSONValue ? T[P] : T[P] extends NotAssignableToJson ? never : JSONCompatible<T[P]>;
|
|
19
19
|
};
|
|
20
|
-
export type DeepReadonly<T> =
|
|
21
|
-
readonly [
|
|
22
|
-
}
|
|
20
|
+
export type DeepReadonly<T> = T extends Map<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>> : T extends Set<infer S> ? ReadonlySet<DeepReadonly<S>> : T extends object ? {
|
|
21
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
22
|
+
} : T;
|
|
23
23
|
export declare const getParams: (strOrSearchParams?: string | URLSearchParams) => URLSearchParams;
|
|
24
24
|
export type UnknownObj = {
|
|
25
25
|
[key: string]: unknown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "state-in-url",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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": [
|
|
@@ -41,19 +41,26 @@
|
|
|
41
41
|
"files": [
|
|
42
42
|
"dist"
|
|
43
43
|
],
|
|
44
|
-
"main": "dist/index.
|
|
45
|
-
"type": "module",
|
|
44
|
+
"main": "dist/index.cjs",
|
|
46
45
|
"exports": {
|
|
47
46
|
"import": {
|
|
48
47
|
"types": "./dist/index.d.ts",
|
|
49
|
-
"default": "./dist/index.
|
|
48
|
+
"default": "./dist/index.mjs"
|
|
49
|
+
},
|
|
50
|
+
"node": {
|
|
51
|
+
"types": "./dist/index.d.ts",
|
|
52
|
+
"default": "./dist/index.mjs"
|
|
53
|
+
},
|
|
54
|
+
"require": {
|
|
55
|
+
"types": "./dist/index.d.ts",
|
|
56
|
+
"default": "./dist/index.cjs"
|
|
50
57
|
},
|
|
51
58
|
"module": {
|
|
52
|
-
"types": "./dist/index.
|
|
59
|
+
"types": "./dist/index.mjs",
|
|
53
60
|
"default": "./dist/index.d.ts"
|
|
54
61
|
},
|
|
55
62
|
"default": {
|
|
56
|
-
"types": "./dist/index.
|
|
63
|
+
"types": "./dist/index.mjs",
|
|
57
64
|
"default": "./dist/index.d.ts"
|
|
58
65
|
}
|
|
59
66
|
},
|
|
@@ -80,7 +87,7 @@
|
|
|
80
87
|
"cleanup": "rm -rf dist",
|
|
81
88
|
"setup": "playwright install --with-deps",
|
|
82
89
|
"cleanupDist": "wireit",
|
|
83
|
-
"prepack": "npm run test:unit && npm run build",
|
|
90
|
+
"prepack": "npm run test:unit && npm run build && npm run build:demo",
|
|
84
91
|
"prepare": "npx husky"
|
|
85
92
|
},
|
|
86
93
|
"wireit": {
|
package/dist/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import t from"react";import{useRouter as e}from"next/navigation";const n=t=>{const e=null===t,n=Array.isArray(t),r=t instanceof Date;return(e?"null":r&&"date")||n&&"array"||!e&&!r&&!n&&"object"==typeof t&&"object"||typeof t},r=()=>"undefined"==typeof window,c=t=>new URLSearchParams("string"==typeof t?t:t?.toString?.()||""),o=(t,e)=>JSON.stringify(t)===JSON.stringify(e),s={string:"◖",boolean:"🗵",null:"∙null",undefined:"∙undefined",number:"∓",date:"⏲"};function a(t){switch(n(t)){case"function":case"symbol":return"";case"date":return s.date+t.toISOString();case"string":return t.match(/^◖/)?encodeURIComponent(t):`${s.string}${encodeURIComponent(t)}`;case"number":return s.number+String(t);case"boolean":return s.boolean+String(t);case"object":case"array":return JSON.stringify(t,i).replaceAll('"',"'");case"null":return s.null;case"undefined":return s.undefined;default:return String(t)}}function u(t,e){return function(t,e){try{return JSON.parse(t,g)}catch{const n=l(t);return n!==f?n:e}}(t.replaceAll("'",'"'),e)}const i=(t,e)=>{const r=n(e);return t&&"object"!==r&&"array"!==r?a(e):e},l=t=>t===s.null?null:t!==s.undefined?t?.startsWith?.(s.number)?Number.parseFloat(t.replace(s.number,"")):t?.startsWith?.(s.boolean)?!!t.includes("true"):t?.startsWith?.(s.date)?new Date(t.slice(1)):t?.startsWith?.(s.string)?decodeURIComponent(t).replace(/^◖/,""):f:void 0,f=Symbol("isError"),g=(t,e)=>{const n="string"==typeof e,r=n&&l(e);return r===f?e:t&&n?r:e};function p(t,e){return{...e,...y(JSON.stringify(t),e)}}const d=(t,e)=>{const n="string"==typeof e,r=n&&u(e?.replaceAll?.("'",'"'));return r===f?e:t&&n?r:e};function y(t,e){try{return JSON.parse(t,d)}catch{const n=l(t);return n!==f?n:e}}function b(t,e,n){const r=c(n);return Object.entries(t||{}).forEach((([t,n])=>{const c=e?.[t];JSON.stringify(n)!==JSON.stringify(c)&&r.set(t,a(n))})),r.toString()}function S(t,e){return{...e||{},...Object.fromEntries([...c(t).entries()].map((([t,n])=>{const r=e?.[t];return[t,u(n,r)??r]})))}}function m(e){const r=t.useCallback((function(t,r){return"object"===n(t)?b(t,e,r):""}),[e]);return{parse:t.useCallback((function(t){return S(t,e)}),[e]),stringify:r}}const h=new WeakMap,w=new WeakMap;function O(n,c){const{state:s,getState:a,setState:u,stringify:i}=function(e){const n=t.useRef(e),{parse:c,stringify:s}=m(n.current),[a,u]=t.useState((()=>{const t=h.get(n.current),e=c(r()?"":window.location.search);return t||h.set(n.current,e),e}));return t.useInsertionEffect((()=>{const t=w.get(n.current)||[],r=()=>{u(h.get(n.current)||e)};return w.set(n.current,t.concat(r)),()=>{const t=w.get(n.current)||[];w.set(n.current,t.filter((t=>t!==r)))}}),[]),{state:a,getState:t.useCallback((()=>h.get(n.current)||n.current),[]),setState:t.useCallback((t=>{const e=h.get(n.current)||n.current;if("function"==typeof t){const r=t(e);if(o(e,r))return!1;h.set(n.current,r),(w.get(n.current)||[]).forEach((t=>{t()}))}else{if(o(e,t))return!1;h.set(n.current,t),(w.get(n.current)||[]).forEach((t=>{t()}))}}),[]),parse:c,stringify:s}}(n),l=e();return{updateUrl:t.useCallback(((t,e)=>{const n=window.location.search,r=`${window.location.pathname}${n.length?"?":""}${n}`,c=i("function"==typeof t?t(a()):a()),o=`${window.location.pathname}${c.length?"?":""}${c}`;if(r!==o){const{replace:t,...n}=e||{};l[t?"replace":"push"](o,{scroll:!1,...n})}}),[l,i,a]),updateState:u,state:r()?p(c,n):s}}export{u as decode,S as decodeState,a as encode,b as encodeState,m as useUrlEncode,O as useUrlState};
|