state-in-url 0.2.3 → 0.2.5
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 +26 -11
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/urlstate/state.d.ts +0 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -25,8 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
<h4 align="center">Don't hesitate to open issue if you found a bug</h4>
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
https://github.com/asmyshlyaev177/state-in-url/assets/19854148/10456887-f149-4745-b3f1-e799e70b16cd
|
|
28
|
+

|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
<a href="https://state-in-url-asmyshlyaev177.vercel.app/" target="_blank">DEMO</a>
|
|
@@ -56,6 +55,7 @@ Add a ⭐️ to support the project!
|
|
|
56
55
|
- 📘 **Typescript support and type Safety**: Preserves data types and structure, enhances developer experience with IDE suggestions, strong typing and JSDoc comments
|
|
57
56
|
- ⚛️ **Framework Flexibility**: Separate hooks for Next.js and React.js applications, and functions for pure JS
|
|
58
57
|
- ⚙ **Well tested**: Unit tests and Playwright tests
|
|
58
|
+
- ⚡ **Fast**: Minimal rerenders
|
|
59
59
|
- 🪶 **Lightweight**: Zero dependencies for a smaller footprint
|
|
60
60
|
|
|
61
61
|
|
|
@@ -96,12 +96,21 @@ set `"moduleResolution": "Node16"` or `"moduleResolution": "NodeNext"` in your `
|
|
|
96
96
|
|
|
97
97
|
#### Basic
|
|
98
98
|
|
|
99
|
+
1. Define state shape
|
|
100
|
+
```typescript
|
|
101
|
+
// countState.ts
|
|
102
|
+
// State shape should be stored in a constant, don't pass an object directly
|
|
103
|
+
export const countState: CountState = { count: 0 }
|
|
104
|
+
|
|
105
|
+
type CountState = { count: number }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
2. Import it and use
|
|
99
109
|
```typescript
|
|
100
110
|
'use client'
|
|
101
111
|
import { useUrlState } from 'state-in-url';
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
const countState: { count: number } = { count: 0 };
|
|
113
|
+
import { countState } from './countState';
|
|
105
114
|
|
|
106
115
|
function MyComponent() {
|
|
107
116
|
// for use searchParams from server component
|
|
@@ -140,24 +149,28 @@ function MyComponent() {
|
|
|
140
149
|
#### With complex state shape
|
|
141
150
|
|
|
142
151
|
```typescript
|
|
143
|
-
'use client'
|
|
144
|
-
import { useUrlState } from 'state-in-url';
|
|
145
|
-
|
|
146
152
|
interface UserSettings {
|
|
147
153
|
theme: 'light' | 'dark';
|
|
148
154
|
fontSize: number;
|
|
149
155
|
notifications: boolean;
|
|
150
156
|
}
|
|
151
157
|
|
|
152
|
-
const
|
|
158
|
+
export const userSettings: UserSettings {
|
|
153
159
|
theme: 'light',
|
|
154
160
|
fontSize: 16,
|
|
155
161
|
notifications: true,
|
|
156
162
|
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
'use client'
|
|
167
|
+
import { useUrlState } from 'state-in-url';
|
|
168
|
+
|
|
169
|
+
import { userSettings } from './userSettings';
|
|
157
170
|
|
|
158
171
|
function SettingsComponent() {
|
|
159
172
|
// `state` will infer from UserSettings type!
|
|
160
|
-
const { state, updateUrl } = useUrlState(
|
|
173
|
+
const { state, updateUrl } = useUrlState(userSettings);
|
|
161
174
|
|
|
162
175
|
const toggleTheme = () => {
|
|
163
176
|
updateUrl(current => ({
|
|
@@ -243,11 +256,14 @@ Accepts optional `defaultState` argument.
|
|
|
243
256
|
```typescript
|
|
244
257
|
import { useUrlEncode } from 'state-in-url';
|
|
245
258
|
|
|
259
|
+
const ageState = { age: 0 };
|
|
260
|
+
|
|
246
261
|
const Component = () => {
|
|
247
262
|
const { parse, stringify } = useUrlEncode();
|
|
248
263
|
|
|
249
264
|
const str = stringify({ age: 36 }); // age=∓36
|
|
250
|
-
const obj = parse(str); // { age: 36 }
|
|
265
|
+
const obj = parse(str, ageState); // { age: 36 }
|
|
266
|
+
// Or const obj = parse(str); // { age: 36 }
|
|
251
267
|
|
|
252
268
|
const currentParams = parse(window.location.search);
|
|
253
269
|
// OR
|
|
@@ -328,7 +344,6 @@ Go to [localhost:3000](http://localhost:3000)
|
|
|
328
344
|
## Contact & Support
|
|
329
345
|
|
|
330
346
|
- Create a [GitHub issue](https://github.com/asmyshlyaev177/state-in-url/issues) for bug reports, feature requests, or questions
|
|
331
|
-
- Add a ⭐️ [star on GitHub](https://github.com/asmyshlyaev177/state-in-url) to support the project!
|
|
332
347
|
|
|
333
348
|
## [Changelog](https://github.com/asmyshlyaev177/state-in-url/blob/main/CHANGELOG.md)
|
|
334
349
|
|
package/dist/index.cjs
CHANGED
|
@@ -1 +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
|
|
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 o=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},c=()=>"undefined"==typeof window,s=e=>new URLSearchParams("string"==typeof e?e:e?.toString?.()||""),u=(e,t)=>JSON.stringify(e)===JSON.stringify(t),a={string:"◖",boolean:"🗵",null:"∙null",undefined:"∙undefined",number:"∓",date:"⏲"};function i(e){switch(o(e)){case"function":case"symbol":return"";case"date":return a.date+e.toISOString();case"string":return e.match(/^◖/)?encodeURIComponent(e):`${a.string}${encodeURIComponent(e)}`;case"number":return a.number+String(e);case"boolean":return a.boolean+String(e);case"object":case"array":return JSON.stringify(e,f).replaceAll('"',"'");case"null":return a.null;case"undefined":return a.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=o(t);return e&&"object"!==n&&"array"!==n?i(t):t},d=e=>e===a.null?null:e!==a.undefined?e?.startsWith?.(a.number)?Number.parseFloat(e.replace(a.number,"")):e?.startsWith?.(a.boolean)?!!e.includes("true"):e?.startsWith?.(a.date)?new Date(e.slice(1)):e?.startsWith?.(a.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=s(n);return Object.entries(e||{}).forEach((([e,n])=>{const o=t?.[e];JSON.stringify(n)!==JSON.stringify(o)&&r.set(e,i(n))})),r.toString()}function w(e,t){return{...t||{},...Object.fromEntries([...s(e).entries()].map((([e,n])=>{const r=t?.[e];return[e,l(n,r)??r]})))}}function h(e){const t=r.default.useCallback((function(t,n){return"object"===o(t)?S(t,e,n):""}),[e]);return{parse:r.default.useCallback((function(t){return w(t,e)}),[e]),stringify:t}}const m=new WeakMap,O=new WeakMap;exports.decode=l,exports.decodeState=w,exports.encode=i,exports.encodeState=S,exports.useUrlEncode=h,exports.useUrlState=function(e,n){const{state:o,getState:s,setState:a,stringify:i}=function(e){const t=r.default.useRef(e),{parse:n,stringify:o}=h(t.current),[s,a]=r.default.useState((()=>{const e=m.get(t.current),r=n(c()?"":window.location.search);return e||m.set(t.current,r),r})),i=r.default.useCallback((e=>{const n=m.get(t.current)||t.current;if("function"==typeof e){const r=e(n);if(u(n,r))return!1;m.set(t.current,r),(O.get(t.current)||[]).forEach((e=>{e()}))}else{if(u(n,e))return!1;m.set(t.current,e),(O.get(t.current)||[]).forEach((e=>{e()}))}}),[]);return r.default.useInsertionEffect((()=>{const r=O.get(t.current)||[],o=()=>{a(m.get(t.current)||e)};O.set(t.current,r.concat(o));const c=()=>{const e=n(window.location.search);i(e)},s="popstate";return window.addEventListener(s,c),()=>{const e=O.get(t.current)||[];O.set(t.current,e.filter((e=>e!==o))),window.removeEventListener(s,c)}}),[i]),{state:s,getState:r.default.useCallback((()=>m.get(t.current)||t.current),[]),setState:i,stringify:o}}(c()?function(e,t){return{...t,...b(JSON.stringify(e),t)}}(n,e):e),l=t.useRouter();return{updateUrl:r.default.useCallback(((e,t)=>{a(e??s());const n=window.location.search,r=`${window.location.pathname}${n.length&&!n.includes("?")?"?":""}${n}`,o=i("function"==typeof e?e(s()):e??s()),c=`${window.location.pathname}${o.length?"?":""}${o}`;if(r!==c){const{replace:e,...n}=t||{};l[e?"replace":"push"](c,{scroll:!1,...n})}}),[l,i,s]),updateState:a,state:o}};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +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,
|
|
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,d)}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"),d=(t,e)=>{const n="string"==typeof e,r=n&&l(e);return r===f?e:t&&n?r:e};const g=(t,e)=>{const n="string"==typeof e,r=n&&u(e?.replaceAll?.("'",'"'));return r===f?e:t&&n?r:e};function p(t,e){try{return JSON.parse(t,g)}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,w=new WeakMap;function h(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})),i=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),(w.get(n.current)||[]).forEach((t=>{t()}))}else{if(o(e,t))return!1;m.set(n.current,t),(w.get(n.current)||[]).forEach((t=>{t()}))}}),[]);return t.useInsertionEffect((()=>{const t=w.get(n.current)||[],r=()=>{u(m.get(n.current)||e)};w.set(n.current,t.concat(r));const o=()=>{const t=c(window.location.search);i(t)},s="popstate";return window.addEventListener(s,o),()=>{const t=w.get(n.current)||[];w.set(n.current,t.filter((t=>t!==r))),window.removeEventListener(s,o)}}),[i]),{state:a,getState:t.useCallback((()=>m.get(n.current)||n.current),[]),setState:i,stringify:s}}(r()?function(t,e){return{...e,...p(JSON.stringify(t),e)}}(c,n):n),l=e();return{updateUrl:t.useCallback(((t,e)=>{u(t??a());const n=window.location.search,r=`${window.location.pathname}${n.length&&!n.includes("?")?"?":""}${n}`,c=i("function"==typeof t?t(a()):t??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,h as useUrlState};
|
package/dist/urlstate/state.d.ts
CHANGED
|
@@ -3,6 +3,5 @@ export declare function useState<T>(defaultState: JSONCompatible<T>): {
|
|
|
3
3
|
state: JSONCompatible<T>;
|
|
4
4
|
getState: () => JSONCompatible<T>;
|
|
5
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
6
|
stringify: (state: JSONCompatible<T> | DeepReadonly<JSONCompatible<T>>, paramsToKeep?: string | URLSearchParams | undefined) => string;
|
|
8
7
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "state-in-url",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
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": [
|
|
@@ -75,6 +75,7 @@
|
|
|
75
75
|
"scripts": {
|
|
76
76
|
"test:unit": "wireit",
|
|
77
77
|
"test:int": "playwright test",
|
|
78
|
+
"test:int:watch": "npx chokidar-cli 'packages/**/*.ts' 'packages/**/*.tsx' -i '.next' -t 500 -c 'npx playwright test -j 2 --project=chromium'",
|
|
78
79
|
"test": "wireit",
|
|
79
80
|
"dev": "wireit",
|
|
80
81
|
"dev:lib": "wireit",
|
|
@@ -82,7 +83,7 @@
|
|
|
82
83
|
"bump": "npx standard-version --no-verify",
|
|
83
84
|
"bump:ex": "npm i state-in-url@latest --workspace=packages/example-nextjs",
|
|
84
85
|
"build": "wireit",
|
|
85
|
-
"build:demo": "cd packages/example-nextjs &&
|
|
86
|
+
"build:demo": "cd packages/example-nextjs && npm run build",
|
|
86
87
|
"start:demo": "npm run build && cd packages/example-nextjs && npm run build && npm run start",
|
|
87
88
|
"cleanup": "rm -rf dist",
|
|
88
89
|
"setup": "playwright install --with-deps",
|
|
@@ -97,7 +98,7 @@
|
|
|
97
98
|
"test:unit",
|
|
98
99
|
"dev:nextjs"
|
|
99
100
|
],
|
|
100
|
-
"command": "npx playwright test --project=chromium"
|
|
101
|
+
"command": "npx playwright test --project=chromium -j 2"
|
|
101
102
|
},
|
|
102
103
|
"test:unit": {
|
|
103
104
|
"command": "npx jest"
|
|
@@ -177,6 +178,7 @@
|
|
|
177
178
|
"@types/node": "^20.11.24",
|
|
178
179
|
"@types/react": "^18.3.3",
|
|
179
180
|
"@types/react-dom": "^18.3.0",
|
|
181
|
+
"chokidar-cli": "^3.0.0",
|
|
180
182
|
"commitizen": "^4.3.0",
|
|
181
183
|
"cross-env": "^7.0.3",
|
|
182
184
|
"cz": "^1.8.2",
|