state-in-url 4.0.8 → 4.1.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 CHANGED
@@ -168,10 +168,11 @@ import { userState } from './userState';
168
168
  function MyComponent() {
169
169
  // can pass `replace` arg, it's control will `setUrl` will use `rounter.push` or `router.replace`, default replace=true
170
170
  // can pass `searchParams` from server components
171
- const { urlState, setUrl, setState } = useUrlState(userState);
171
+ const { urlState, setUrl, setState, reset } = useUrlState(userState);
172
172
 
173
173
  return (
174
174
  <div>
175
+ // urlState.name will return default value from `userState` if url empty
175
176
  <input value={urlState.name}
176
177
  // same api as React.useState, e.g. setUrl(currVal => currVal + 1)
177
178
  onChange={(ev) => setUrl({ name: ev.target.value }) }
@@ -186,7 +187,7 @@ function MyComponent() {
186
187
  onBlur={() => setUrl()}
187
188
  />
188
189
 
189
- <button onClick={() => setUrl(userState)}>
190
+ <button onClick={reset}>
190
191
  Reset
191
192
  </button>
192
193
 
@@ -1,3 +1,4 @@
1
+ import { useRouter } from "next/navigation";
1
2
  import { type JSONCompatible } from "../../utils";
2
3
  /**
3
4
  * @deprecated .
@@ -17,22 +18,32 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
17
18
  * NextJS hook. Returns `urlState`, `setState`, and `setUrl` functions
18
19
  *
19
20
  * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
20
- * @param {Object} params - Object with other parameters
21
+ * @param {Object} params - Object with other parameters, including params from App router
22
+ * @param {boolean} params.replace replace URL or push, default `true`
23
+ * @param {boolean} params.useHistory use window.history for navigation, default `true`, no _rsc requests https://github.com/vercel/next.js/discussions/59167
21
24
  * @param {?SearchParams<T>} params.searchParams searchParams from Next server component
22
- * @param {boolean} params.useHistory use window.history for navigation, default true, no _rsc requests https://github.com/vercel/next.js/discussions/59167
25
+ * @param {boolean} params.scroll reset scroll, default `false`
26
+ * @returns {Object} [result] State and callbacks
27
+ * @returns {Object} [result.state] - current state object
28
+ * @returns {Function} [result.setUrl] - function to update state and url
29
+ * @returns {Function} [result.setState] - function to update state only
30
+ * @returns {Function} [result.reset] - function to reset state and url to default
31
+
23
32
  *
24
33
  * * Example:
25
34
  * ```ts
26
35
  * export const form = { name: '', age: 0 };
27
36
  * const { urlState, setState, setUrl } = useUrlState(form);
28
37
  * // for nextjs server components
29
- * const { urlState, setState, setUrl } = useUrlState(form, { searchParams });
38
+ * const { urlState, setState, setUrl, reset } = useUrlState(form, { searchParams });
30
39
  *
31
40
  * setState({ name: 'test' });
32
- * // by default it's uses router.push with scroll: false
33
41
  * setUrl({ name: 'test' }, { replace: true, scroll: true });
34
- * // similar to React.useState
35
42
  * setUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
43
+ * // reset state and url
44
+ * reset();
45
+ * reset({ replace: true });
46
+ * // same as setState(form) with setUrl(form)
36
47
  * ```
37
48
  *
38
49
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState#api}
@@ -40,8 +51,16 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
40
51
  export declare function useUrlState<T extends JSONCompatible>(defaultState: T, params?: Params): {
41
52
  urlState: T;
42
53
  setState: (value: Partial<T> | ((currState: T) => T)) => void;
43
- setUrl: (value?: Partial<T> | ((currState: T) => T)) => void;
54
+ setUrl: (value?: Partial<T> | ((currState: T) => T), options?: Options) => void;
55
+ reset: (options?: Options & {
56
+ [key: string]: unknown;
57
+ }) => void;
44
58
  };
59
+ type Router = ReturnType<typeof useRouter>;
60
+ type RouterOptions = NonNullable<Parameters<Router["push"]>[1] | Parameters<Router["replace"]>[1]>;
61
+ interface Options extends RouterOptions {
62
+ replace?: boolean;
63
+ }
45
64
  interface OldParams<T> {
46
65
  defaultState: T;
47
66
  searchParams?: object;
@@ -1 +1 @@
1
- import{useRouter as t,useSearchParams as e}from"next/navigation";import a from"react";import{parseSPObj as r}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as l,isSSR as o,filterUnknownParams as c,filterUnknownParamsClient as u}from"../../utils.mjs";function i(i,m){const f="defaultState"in i?i.defaultState:i,n="defaultState"in i?i.searchParams:m?.searchParams,S="defaultState"in i?i.useHistory:m?.useHistory,d="defaultState"in i?{scroll:i.scroll,replace:i.replace}:{scroll:m?.scroll,replace:m?.replace},j=void 0===S||S?l:t(),{state:U,updateState:b,updateUrl:g,getState:P}=s(f,j,(({parse:t})=>o()?r(c(f,n),f):t(u(f)))),h=a.useCallback(((t,e)=>g(t,{...p,...d,...e})),[g,d]),v=e();return a.useEffect((()=>{b(c(f,r(Object.fromEntries([...v.entries()]),f)))}),[v]),{setState:b,updateState:b,setUrl:h,updateUrl:h,urlState:U,state:U,getState:P}}const p={replace:!0,scroll:!1};export{i as useUrlState};
1
+ import{useRouter as e,useSearchParams as t}from"next/navigation";import a from"react";import{parseSPObj as r}from"../../parseSPObj.mjs";import{useUrlStateBase as s}from"../../useUrlStateBase/useUrlStateBase.mjs";import{routerHistory as l,isSSR as o,filterUnknownParams as c,filterUnknownParamsClient as u}from"../../utils.mjs";function i(i,p){const n="defaultState"in i?i.defaultState:i,f="defaultState"in i?i.searchParams:p?.searchParams,S="defaultState"in i?i.useHistory:p?.useHistory,d="defaultState"in i?{scroll:i.scroll,replace:i.replace}:{scroll:p?.scroll,replace:p?.replace},j=void 0===S||S?l:e(),{state:U,updateState:b,updateUrl:g,reset:P,getState:h}=s(n,j,(({parse:e})=>o()?r(c(n,f),n):e(u(n)))),k=a.useMemo((()=>({...m,...d})),[]),v=a.useCallback(((e,t)=>g(e,{...k,...t})),[g]),x=t();a.useEffect((()=>{b(c(n,r(Object.fromEntries([...x.entries()]),n)))}),[x]);const y=a.useCallback((e=>{P({...k,...e})}),[P]);return{setState:b,updateState:b,setUrl:v,updateUrl:v,urlState:U,state:U,reset:y,getState:h}}const m={replace:!0,scroll:!1};export{i as useUrlState};
@@ -20,7 +20,14 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
20
20
  * @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
21
21
  * @param {Object} params - Object with other parameters
22
22
  * @param {NavigateOptions} params.NavigateOptions See type from `react-router-dom`
23
- * @param {boolean} params.useHistory use window.history for navigation
23
+ * @param {boolean} params.replace replace URL or push, default `true`
24
+ * @param {boolean} params.useHistory use window.history for navigation, default `false`
25
+ * @param {boolean} params.preventScrollReset keep scroll position, default `true`
26
+ * @returns {Object} [result] State and callbacks
27
+ * @returns {Object} [result.state] - current state object
28
+ * @returns {Function} [result.setUrl] - function to update state and url
29
+ * @returns {Function} [result.setState] - function to update state only
30
+ * @returns {Function} [result.reset] - function to reset state and url to default
24
31
  * * Example:
25
32
  * ```ts
26
33
  * export const form = { name: '', age: 0 };
@@ -30,6 +37,10 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
30
37
  * setUrl({ name: 'test' }, { replace: true });
31
38
  * // similar to React.useState
32
39
  * setUrl(curr => ({ ...curr, name: 'test' }), { replace: true });
40
+ * // reset state and url
41
+ * reset();
42
+ * reset({ replace: true });
43
+ * // same as setState(form) with setUrl(form)
33
44
  * ```
34
45
  *
35
46
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react-router/useUrlState#api}
@@ -37,7 +48,10 @@ export declare function useUrlState<T extends JSONCompatible>({ defaultState: T,
37
48
  export declare function useUrlState<T extends JSONCompatible>(defaultState: T, params?: Params): {
38
49
  urlState: T;
39
50
  setState: (value: Partial<T> | ((currState: T) => T)) => void;
40
- setUrl: (value?: Partial<T> | ((currState: T) => T)) => void;
51
+ setUrl: (value?: Partial<T> | ((currState: T) => T), options?: Params) => void;
52
+ reset: (options?: NavigateOptions & {
53
+ [key: string]: unknown;
54
+ }) => void;
41
55
  };
42
56
  type OldParams<T> = {
43
57
  defaultState: T;
@@ -1 +1 @@
1
- import e from"react";import{useNavigate as t,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 l,filterUnknownParamsClient as o,assignValue as p,filterUnknownParams as u}from"../../utils.mjs";function c(c,m){const n="defaultState"in c?c.defaultState:c,i="defaultState"in c?c.useHistory:m?.useHistory,f="defaultState"in c?{replace:c.replace,preventScrollReset:c.preventScrollReset}:{replace:m?.replace,preventScrollReset:m?.preventScrollReset},d=t(),j=e.useMemo((()=>i?l:{replace:(e,t)=>d(e,{...S,...f,...t}),push:(e,t)=>d(e,{...S,...f,...t})}),[d,f]),{state:v,updateState:R,updateUrl:U,getState:b}=s(n,j,(({parse:e})=>e(o(n)))),g=e.useCallback(((e,t)=>U(e,{...S,...f,...t})),[f]),[y]=r();return e.useEffect((()=>{R(p(n,u(n,a(Object.fromEntries([...y.entries()]),n))))}),[y]),{setState:R,updateState:R,setUrl:g,updateUrl:g,urlState:v,state:v,getState:b}}const S={replace:!0,preventScrollReset:!0};export{c as useUrlState};
1
+ import e from"react";import{useNavigate as t,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 l,filterUnknownParamsClient as o,assignValue as p,filterUnknownParams as u}from"../../utils.mjs";function c(c,m){const n="defaultState"in c?c.defaultState:c,i="defaultState"in c?c.useHistory:m?.useHistory,f="defaultState"in c?{replace:c.replace,preventScrollReset:c.preventScrollReset}:{replace:m?.replace,preventScrollReset:m?.preventScrollReset},d=e.useMemo((()=>({...S,...f})),[]),j=t(),v=e.useMemo((()=>i?l:{replace:(e,t)=>j(e,{...d,...t}),push:(e,t)=>j(e,{...d,...t})}),[j]),{state:R,updateState:U,updateUrl:b,getState:g,reset:k}=s(n,v,(({parse:e})=>e(o(n)))),y=e.useCallback(((e,t)=>b(e,{...d,...t})),[b]),[B]=r();e.useEffect((()=>{U(p(n,u(n,a(Object.fromEntries([...B.entries()]),n))))}),[B]);const C=e.useCallback((e=>{k({...d,...e})}),[k]);return{setState:U,updateState:U,setUrl:y,updateUrl:y,urlState:R,state:R,reset:C,getState:g}}const S={replace:!0,preventScrollReset:!0};export{c as useUrlState};
@@ -7,16 +7,28 @@ import { type JSONCompatible, type Router } from "../utils";
7
7
  * @param {T} defaultState - An object representing the default state values.
8
8
  * @param {Router} router - Router object with [push] and [replace] methods.
9
9
  * @param {Function} [getInitialState] - Optional function to get the initial state, passes `parse`, and `filterUnknownParams` helpers
10
- * @returns {Object} An object containing `state`, `getState`, `updateState`, and `updateUrl` properties.
10
+ * @returns {Object} [result] State and callbacks
11
+ * @returns {Object} [result.state] - current state object
12
+ * @returns {Function} [result.updateUrl] - function to update state and url
13
+ * @returns {Function} [result.updateState] - function to update state only
14
+ * @returns {Function} [result.reset] - function to reset state and url to default
11
15
  *
12
16
  *
13
17
  * * Example:
14
18
  * ```ts
15
19
  * export const form = { name: '' };
16
20
  * const router = { push: () => {}, replace: () => {} };
17
- * const { state, updateState, updateUrl } = useUrlStateBase(form, router, ({ parse, filterClientSP }) =>
21
+ * const { state, updateState, updateUrl, reset } = useUrlStateBase(form, router, ({ parse, filterClientSP }) =>
18
22
  * isSSR() ? getServerState() : getClientState()
19
23
  * );
24
+ *
25
+ * updateState({ name: 'John' });
26
+ * updateState(curr => ({ ...curr, name: 'John' }));
27
+ * updateUrl({ name: 'John' }, { replace: true });
28
+ * updateUrl(curr => ({ ...curr, name: 'John' }), { replace: true });
29
+ * reset()
30
+ * reset({ replace: true })
31
+ *
20
32
  * ```
21
33
  *
22
34
  * * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/integrations/packages/urlstate/useUrlStateBase#api}
@@ -27,6 +39,7 @@ export declare function useUrlStateBase<T extends JSONCompatible>(defaultState:
27
39
  updateState: (value: Partial<T> | ((currState: T) => T)) => void;
28
40
  updateUrl: (value?: Parameters<(value: Partial<T> | ((currState: T) => T)) => void>[0], options?: Options) => void;
29
41
  state: T;
42
+ reset: (options?: Options) => void;
30
43
  getState: () => T;
31
44
  };
32
45
  interface OptionsObject {
@@ -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:l}=o(s),{state:p,getState:d,setState:h}=n(s,(()=>i?.({parse:u})||s));e((()=>{const t=()=>{const t=u(r(s));h(t)};return window.addEventListener(a,t),()=>{window.removeEventListener(a,t)}}),[h]);const m=t.useRef([]),w=t.useCallback(((t,e)=>{const n="function"==typeof t?t(d()):t?{...d(),...t}:d(),o=l(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;h(n);const i=e?.replace;delete e?.replace,m.current.push([i?"replace":"push",r,e]),1===m.current.length&&queueMicrotask((()=>{for(;m.current.length;){const t=m.current.shift();t&&t?.[1]!==a?.[1]&&(a=t)}const[t,e,n]=a||{};a=void 0,t&&c[t](e,n)}))}),[c,l,d]);return{updateState:h,updateUrl:w,state:p,getState:d}}const a="popstate";export{s as useUrlStateBase};
1
+ import e from"react";import{useInsertionEffect as t}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:l}=o(s),{state:p,getState:d,setState:h}=n(s,(()=>i?.({parse:u})||s));t((()=>{const e=()=>{const e=u(r(s));h(e)};return window.addEventListener(a,e),()=>{window.removeEventListener(a,e)}}),[h]);const m=e.useRef([]),w=e.useCallback(((e,t)=>{const n="function"==typeof e?e(d()):e?{...d(),...e}:d(),o=l(n,function(e){const t=Object.keys(e),n=window.location.search,o=new URLSearchParams(n),r=new URLSearchParams;return o.forEach(((e,n)=>!t.includes(n)&&r.set(n,e))),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;h(n);const i=t?.replace;delete t?.replace,m.current.push([i?"replace":"push",r,t]),1===m.current.length&&queueMicrotask((()=>{for(;m.current.length;){const e=m.current.shift();e&&e?.[1]!==a?.[1]&&(a=e)}const[e,t,n]=a||{};a=void 0,e&&c[e](t,n)}))}),[c,l,d]),f=e.useCallback((e=>{w(s,e)}),[w]);return{updateState:h,updateUrl:w,state:p,reset:f,getState:d}}const a="popstate";export{s as useUrlStateBase};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "state-in-url",
3
- "version": "4.0.8",
3
+ "version": "4.1.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
  "repository": {