state-in-url 3.1.0 → 4.0.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.md +11 -3
- package/dist/next/useUrlState/useUrlState.d.ts +3 -1
- package/dist/next/useUrlState/useUrlState.mjs +1 -1
- package/dist/react-router/useUrlState/useUrlState.d.ts +3 -1
- package/dist/react-router/useUrlState/useUrlState.mjs +1 -1
- package/dist/useUrlStateBase/useUrlStateBase.d.ts +1 -5
- package/dist/useUrlStateBase/useUrlStateBase.mjs +1 -1
- package/dist/utils.d.ts +5 -0
- package/dist/utils.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -146,7 +146,7 @@ import { userState } from './userState';
|
|
|
146
146
|
function MyComponent() {
|
|
147
147
|
// can pass `replace` arg, it's control will `setUrl` will use `rounter.push` or `router.replace`, default replace=true
|
|
148
148
|
// can pass `searchParams` from server components
|
|
149
|
-
const { urlState, setUrl } = useUrlState({ defaultState: userState });
|
|
149
|
+
const { urlState, setUrl, setUrlState } = useUrlState({ defaultState: userState });
|
|
150
150
|
|
|
151
151
|
// won't let you to accidently mutate state directly, requires TS
|
|
152
152
|
// urlState.name = 'John' // <- error
|
|
@@ -162,7 +162,9 @@ function MyComponent() {
|
|
|
162
162
|
|
|
163
163
|
// same api as React.useState
|
|
164
164
|
<input value={urlState.name}
|
|
165
|
-
onChange={(ev) => {
|
|
165
|
+
onChange={(ev) => { setUrlState(curr => ({ ...curr, name: ev.target.value })) }}
|
|
166
|
+
// Can update state immediately but sync change to url as needed
|
|
167
|
+
onBlur={() => setUrl()}
|
|
166
168
|
/>
|
|
167
169
|
|
|
168
170
|
<button onClick={() => setUrl(userState)}>
|
|
@@ -408,7 +410,7 @@ import { useUrlState } from 'state-in-url/react-router';
|
|
|
408
410
|
import { form } from './form';
|
|
409
411
|
|
|
410
412
|
function TagsComponent() {
|
|
411
|
-
const { urlState, setUrl } = useUrlState({ defaultState: form });
|
|
413
|
+
const { urlState, setUrl, setUrlState } = useUrlState({ defaultState: form });
|
|
412
414
|
|
|
413
415
|
const onChangeTags = React.useCallback(
|
|
414
416
|
(tag: (typeof tags)[number]) => {
|
|
@@ -436,6 +438,12 @@ function TagsComponent() {
|
|
|
436
438
|
))}
|
|
437
439
|
</div>
|
|
438
440
|
</Field>
|
|
441
|
+
|
|
442
|
+
<input value={urlState.name}
|
|
443
|
+
onChange={(ev) => { setUrlState(curr => ({ ...curr, name: ev.target.value })) }}
|
|
444
|
+
// Can update state immediately but sync change to url as needed
|
|
445
|
+
onBlur={() => setUrl()}
|
|
446
|
+
/>
|
|
439
447
|
</div>
|
|
440
448
|
);
|
|
441
449
|
}
|
|
@@ -5,6 +5,7 @@ import { type DeepReadonly, type JSONCompatible } from "../../utils";
|
|
|
5
5
|
*
|
|
6
6
|
* @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
|
|
7
7
|
* @param {?SearchParams<T>} [searchParams] searchParams from Next server component
|
|
8
|
+
* @param {boolean} [useHistory] use window.history for navigation, no _rsc requests https://github.com/vercel/next.js/discussions/59167
|
|
8
9
|
*
|
|
9
10
|
* * Example:
|
|
10
11
|
* ```ts
|
|
@@ -22,11 +23,12 @@ import { type DeepReadonly, type JSONCompatible } from "../../utils";
|
|
|
22
23
|
*
|
|
23
24
|
* * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState#api}
|
|
24
25
|
*/
|
|
25
|
-
export declare function useUrlState<T extends JSONCompatible>({ defaultState, searchParams, ...opts }: {
|
|
26
|
+
export declare function useUrlState<T extends JSONCompatible>({ defaultState, searchParams, useHistory, ...opts }: {
|
|
26
27
|
defaultState: T;
|
|
27
28
|
searchParams?: object;
|
|
28
29
|
replace?: boolean;
|
|
29
30
|
scroll?: boolean;
|
|
31
|
+
useHistory?: boolean;
|
|
30
32
|
}): {
|
|
31
33
|
/**
|
|
32
34
|
* * Example:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{useRouter as t,useSearchParams as e}from"next/navigation";import
|
|
1
|
+
import{useRouter as t,useSearchParams as e}from"next/navigation";import r from"react";import{parseSPObj as a}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as o,isSSR as m,filterUnknownParams as u,filterUnknownParamsClient as i}from"../../utils.mjs";function l({defaultState:l,searchParams:c,useHistory:n,...f}){const S=void 0===n||n?o:t(),{state:d,updateState:j,updateUrl:U,getState:b}=s(l,S,(({parse:t})=>m()?a(u(l,c),l):t(i(l)))),g=r.useCallback(((t,e)=>{const r={...p,...f,...e};U(t,r)}),[U,f]),v=e();return r.useEffect((()=>{j(u(l,a(Object.fromEntries([...v.entries()]),l)))}),[v]),{setState:j,updateState:j,setUrl:g,updateUrl:g,urlState:d,state:d,getState:b}}const p={replace:!0,scroll:!1};export{l as useUrlState};
|
|
@@ -5,6 +5,7 @@ import { type DeepReadonly, type JSONCompatible } from "../../utils";
|
|
|
5
5
|
*
|
|
6
6
|
* @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
|
|
7
7
|
* @param {NavigateOptions} [NavigateOptions] See type from `react-router-dom`
|
|
8
|
+
* @param {boolean} [useHistory] use window.history for navigation
|
|
8
9
|
* * Example:
|
|
9
10
|
* ```ts
|
|
10
11
|
* export const form = { name: '', age: 0 };
|
|
@@ -18,8 +19,9 @@ import { type DeepReadonly, type JSONCompatible } from "../../utils";
|
|
|
18
19
|
*
|
|
19
20
|
* * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react-router/useUrlState#api}
|
|
20
21
|
*/
|
|
21
|
-
export declare function useUrlState<T extends JSONCompatible>({ defaultState, ...initOpts }: {
|
|
22
|
+
export declare function useUrlState<T extends JSONCompatible>({ defaultState, useHistory, ...initOpts }: {
|
|
22
23
|
defaultState: T;
|
|
24
|
+
useHistory?: boolean;
|
|
23
25
|
} & NavigateOptions): {
|
|
24
26
|
/**
|
|
25
27
|
* * Example:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"react";import{useNavigate as e,useSearchParams as r}from"react-router-dom";import{parseSPObj as a}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{
|
|
1
|
+
import t from"react";import{useNavigate as e,useSearchParams as r}from"react-router-dom";import{parseSPObj as a}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as o,filterUnknownParamsClient as u,assignValue as m,filterUnknownParams as p}from"../../utils.mjs";function l({defaultState:l,useHistory:S,...f}){const i=e(),n=t.useMemo((()=>S?o:{replace:(t,e)=>i(t,{...c,...f,...e}),push:(t,e)=>i(t,{...c,...f,...e})}),[i,f]),{state:d,updateState:j,updateUrl:U,getState:b}=s(l,n,(({parse:t})=>t(u(l)))),g=t.useCallback(((t,e)=>{const r={...c,...f,...e};U(t,r)}),[f]),[B]=r();return t.useEffect((()=>{j(m(l,d,p(l,a(Object.fromEntries([...B.entries()]),l))))}),[B]),{setState:j,updateState:j,setUrl:g,updateUrl:g,urlState:d,state:d,getState:b}}const c={replace:!0,preventScrollReset:!0};export{l as useUrlState};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useUrlEncode } from "../useUrlEncode";
|
|
2
|
-
import { type DeepReadonly, type JSONCompatible } from "../utils";
|
|
2
|
+
import { type DeepReadonly, type JSONCompatible, type Router } from "../utils";
|
|
3
3
|
/**
|
|
4
4
|
* A custom React hook to create custom hooks.
|
|
5
5
|
*
|
|
@@ -35,8 +35,4 @@ interface OptionsObject {
|
|
|
35
35
|
export interface Options extends OptionsObject {
|
|
36
36
|
replace?: boolean;
|
|
37
37
|
}
|
|
38
|
-
type Router = {
|
|
39
|
-
push: (href: string, opts: object) => void;
|
|
40
|
-
replace: (href: string, opts: object) => void;
|
|
41
|
-
};
|
|
42
38
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"react";import{useInsertionEffect as e}from"../useInsertionEffect.mjs";import{useSharedState as n}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as o}from"../useUrlEncode/useUrlEncode.mjs";import{filterUnknownParamsClient as r}from"../utils.mjs";function s(s,c,i){const{parse:u,stringify:
|
|
1
|
+
import t from"react";import{useInsertionEffect as e}from"../useInsertionEffect.mjs";import{useSharedState as n}from"../useSharedState/useSharedState.mjs";import{useUrlEncode as o}from"../useUrlEncode/useUrlEncode.mjs";import{filterUnknownParamsClient as r}from"../utils.mjs";function s(s,c,i){const{parse:u,stringify:h}=o(s),{state:l,getState:m,setState:p}=n(s,(()=>i?.({parse:u})||s));e((()=>{const t=()=>{const t=u(r(s));p(t)};return window.addEventListener(a,t),()=>{window.removeEventListener(a,t)}}),[p]);const d=t.useRef([]),w=t.useCallback(((t,e)=>{const n="function"==typeof t?t(m()):t?{...m(),...t}:m(),o=h(n,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}(s)),r=`${window.location.pathname}${o.length?"?":""}${o}${window.location.hash}`;if(r===`${window.location.pathname}${window.location.search}${window.location.hash}`)return;let a;p(n);const{replace:i,...u}=e||{};d.current.push([i?"replace":"push",r,u]),1===d.current.length&&queueMicrotask((()=>{for(;d.current.length;){const t=d.current.shift();t&&t?.[1]!==a?.[1]&&(a=t)}const[t,e,n]=a||{};a=void 0,t&&c[t](e,n)}))}),[c,h,m]);return{updateState:p,updateUrl:w,state:l,getState:m}}const a="popstate";export{s as useUrlStateBase};
|
package/dist/utils.d.ts
CHANGED
|
@@ -26,3 +26,8 @@ export declare const isEqual: (val1: unknown, val2: unknown) => boolean;
|
|
|
26
26
|
export declare function filterUnknownParamsClient<T extends object>(shape: T): string;
|
|
27
27
|
export declare function filterUnknownParams<T extends object>(shape: T, searchParams?: object): T;
|
|
28
28
|
export declare function assignValue<T extends object>(shape: T, curr: Partial<T>, newVal: Partial<T>): T;
|
|
29
|
+
export interface Router {
|
|
30
|
+
push: (href: string, opts: object) => void;
|
|
31
|
+
replace: (href: string, opts: object) => void;
|
|
32
|
+
}
|
|
33
|
+
export declare const routerHistory: Router;
|
package/dist/utils.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const t=t=>{const n=typeof t,e=null===t,r=Array.isArray(t),
|
|
1
|
+
const t=t=>{const n=typeof t,e=null===t,r=Array.isArray(t),o=t instanceof Date;return(e?"null":o&&"date")||r&&"array"||!e&&!o&&!r&&"object"===n&&"object"||n},n=()=>"undefined"==typeof window,e=t=>new URLSearchParams("string"==typeof t?r(t):t?.toString?.()||""),r=t=>t.split("?")?.[1]||t||"",o=(t,n)=>JSON.stringify(t)===JSON.stringify(n);function i(t){const n=new URLSearchParams;return s(t,[...new URLSearchParams(window.location.search).entries()]).forEach((([t,e])=>n.set(t,e))),n.toString()}function c(t,n){return Object.fromEntries(s(t,Object.entries(n||{})))}function s(t,n){const e=Object.keys(t);return n.filter((([t])=>e.includes(t))).map((([t,n])=>[t.replaceAll("+"," "),n]))}function a(t,n,e){const r=Object.assign({},t,n);return Object.entries(t).forEach((([n])=>{const o=n,i=void 0!==e[o];r[o]=i?e[o]:t[o]})),r}const l={push:t=>{window&&window.history.pushState(null,"",t)},replace:t=>{window&&window.history.replaceState(null,"",t)}};export{a as assignValue,c as filterUnknownParams,i as filterUnknownParamsClient,e as getParams,o as isEqual,n as isSSR,l as routerHistory,t as typeOf};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "state-in-url",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Easily share complex state objects between unrelated React components, preserve types and structure, with TS validation. Deep links and url state synchronization wthout any hasssle or boilerplate.",
|
|
5
5
|
"homepage": "https://state-in-url.dev",
|
|
6
6
|
"keywords": [
|