Skip to content

Commit

Permalink
Merge pull request #29 from asmyshlyaev177/reset_cb
Browse files Browse the repository at this point in the history
`reset` callback for `useUrlState` hooks
  • Loading branch information
asmyshlyaev177 authored Nov 15, 2024
2 parents 59ef5f7 + ad69015 commit 3f00560
Show file tree
Hide file tree
Showing 12 changed files with 221 additions and 31 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,11 @@ import { userState } from './userState';
function MyComponent() {
// can pass `replace` arg, it's control will `setUrl` will use `rounter.push` or `router.replace`, default replace=true
// can pass `searchParams` from server components
const { urlState, setUrl, setState } = useUrlState(userState);
const { urlState, setUrl, setState, reset } = useUrlState(userState);

return (
<div>
// urlState.name will return default value from `userState` if url empty
<input value={urlState.name}
// same api as React.useState, e.g. setUrl(currVal => currVal + 1)
onChange={(ev) => setUrl({ name: ev.target.value }) }
Expand All @@ -186,7 +187,7 @@ function MyComponent() {
onBlur={() => setUrl()}
/>

<button onClick={() => setUrl(userState)}>
<button onClick={reset}>
Reset
</button>

Expand Down
Binary file modified assets/social_banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions packages/example-nextjs14/src/app/Form-for-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const Form = ({
delay?: number;
}) => {
const sp = useSearchParams();
const { urlState, setState, setUrl } = useUrlState(form, {
const { urlState, setState, setUrl, reset } = useUrlState(form, {
searchParams,
replace: sp.get('replace') === 'false' ? false : true,
});
Expand Down Expand Up @@ -47,6 +47,8 @@ export const Form = ({
setUrl((st) => ({ ...st, age: 18 }));
setUrl(urlState, { replace: true });
setUrl((st) => ({ ...st, age: 18 }), { replace: true });
reset()
reset({ replace: false, scroll: true })
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Expand Down Expand Up @@ -132,9 +134,7 @@ export const Form = ({
</Field>

<Button
onClick={() => {
setUrl(form);
}}
onClick={reset}
dataTestId="sync-default"
>
Reset state
Expand Down
8 changes: 4 additions & 4 deletions packages/example-nextjs15/src/app/Form-for-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const Form = ({
searchParams?: object;
}) => {
const sp = useSearchParams();
const { urlState, setState, setUrl } = useUrlState(form, {
const { urlState, setState, setUrl, reset } = useUrlState(form, {
searchParams,
replace: sp.get('replace') === 'false' ? false : true,
});
Expand Down Expand Up @@ -45,6 +45,8 @@ export const Form = ({
setUrl((st) => ({ ...st, age: 18 }));
setUrl(urlState, { replace: true });
setUrl((st) => ({ ...st, age: 18 }), { replace: true });
reset()
reset({ replace: false, scroll: true })
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Expand Down Expand Up @@ -128,9 +130,7 @@ export const Form = ({
</Field>

<Button
onClick={() => {
setUrl(form);
}}
onClick={reset}
dataTestId="sync-default"
>
Reset state
Expand Down
11 changes: 4 additions & 7 deletions packages/example-react-router6/src/Form-for-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useUrlState } from "state-in-url/react-router";

export const Form = ({ className }: { className?: string }) => {
const [sp] = useSearchParams();
const { urlState, setState, setUrl } = useUrlState(form, {
const { urlState, setState, setUrl, reset } = useUrlState(form, {
replace: sp.get("replace") === "false" ? false : true,
});

Expand Down Expand Up @@ -37,6 +37,8 @@ export const Form = ({ className }: { className?: string }) => {
setUrl((st) => ({ ...st, age: 18 }));
setUrl(urlState, { replace: true });
setUrl((st) => ({ ...st, age: 18 }), { replace: true });
reset();
reset({ replace: false, preventScrollReset: true });
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Expand Down Expand Up @@ -127,12 +129,7 @@ export const Form = ({ className }: { className?: string }) => {
</div>
</Field>

<Button
onClick={() => {
setUrl(form);
}}
dataTestId="sync-default"
>
<Button onClick={reset} dataTestId="sync-default">
Reset state
</Button>
<Button
Expand Down
11 changes: 11 additions & 0 deletions packages/urlstate/next/useUrlState/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ A custom React hook that manages state and synchronizes it with URL search param
- `sp?: object` - Optional search params object from Next.js server component.
- `replace?: boolean` - Control will `setUrl` use `replace` or `push` methods on router, default replace=true, can override by `setUrl(stateObj, { replace: false })`
- `useHistory` - Optionally can use window.history for navigation, `true` by default no _rsc requests https://github.com/vercel/next.js/discussions/59167
- `scroll?: boolean` - option from Next.js router push/replace


### Returns:

Expand All @@ -20,6 +22,7 @@ An object containing:
- `urlState: object` - The current state.
- `setState: Function` - Function to update the state without updating the URL.
- `setUrl: Function` - Function to update both the state and the URL.
- `reset: Function` - Function to reset state to default.

### Example:

Expand Down Expand Up @@ -56,3 +59,11 @@ Updates both the state and the URL.
- `value?: T | Partial<T> | (currState: T) => T` - Optional new state value or a function that receives the current state and returns the new state.
- `options?: Options` - Optional options object. When `replace` is true it will use router.replace. Nextjs `scroll` is `false` by default.

## `reset`

Updates both the state and the URL.

### Parameters:

- `options?: Options` - Optional options object. When `replace` is true it will use router.replace. Nextjs `scroll` is `false` by default.

41 changes: 36 additions & 5 deletions packages/urlstate/next/useUrlState/useUrlState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,31 @@ export function useUrlState<T extends JSONCompatible>({
*
* @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
* @param {Object} params - Object with other parameters, including params from App router
* @param {boolean} params.replace replace URL of push, default `true`
* @param {boolean} params.useHistory use window.history for navigation, default true, no _rsc requests https://github.com/vercel/next.js/discussions/59167
* @param {boolean} params.replace replace URL or push, default `true`
* @param {boolean} params.useHistory use window.history for navigation, default `true`, no _rsc requests https://github.com/vercel/next.js/discussions/59167
* @param {?SearchParams<T>} params.searchParams searchParams from Next server component
* @param {boolean} params.scroll reset scroll, default `false`
* @returns {Object} [result] State and callbacks
* @returns {Object} [result.state] - current state object
* @returns {Function} [result.setUrl] - function to update state and url
* @returns {Function} [result.setState] - function to update state only
* @returns {Function} [result.reset] - function to reset state and url to default
*
* * Example:
* ```ts
* export const form = { name: '', age: 0 };
* const { urlState, setState, setUrl } = useUrlState(form);
* // for nextjs server components
* const { urlState, setState, setUrl } = useUrlState(form, { searchParams });
* const { urlState, setState, setUrl, reset } = useUrlState(form, { searchParams });
*
* setState({ name: 'test' });
* setUrl({ name: 'test' }, { replace: true, scroll: true });
* setUrl(curr => ({ ...curr, name: 'test' }), { replace: true, scroll: true });
* // reset state and url
* reset();
* reset({ replace: true });
* // same as setState(form) with setUrl(form)
* ```
*
* * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState#api}
Expand All @@ -68,6 +77,7 @@ export function useUrlState<T extends JSONCompatible>(
value?: Partial<T> | ((currState: T) => T),
options?: Options,
) => void;
reset: (options?: Options & { [key: string]: unknown }) => void;
};

export function useUrlState<T extends JSONCompatible>(
Expand Down Expand Up @@ -102,6 +112,7 @@ export function useUrlState<T extends JSONCompatible>(
state,
updateState,
updateUrl: updateUrlBase,
reset: resetBase,
getState,
} = useUrlStateBase(_defaultState, router, ({ parse }) =>
isSSR()
Expand All @@ -112,10 +123,12 @@ export function useUrlState<T extends JSONCompatible>(
: parse(filterUnknownParamsClient(_defaultState)),
);

const defOpts = React.useMemo(() => ({ ...defaultOptions, ..._opts }), []);

const setUrl = React.useCallback(
(value?: Parameters<typeof updateUrlBase>[0], options?: Options) =>
updateUrlBase(value, { ...defaultOptions, ..._opts, ...options }),
[updateUrlBase, _opts],
updateUrlBase(value, { ...defOpts, ...options }),
[updateUrlBase],
);

const sp = useSearchParams();
Expand All @@ -131,6 +144,13 @@ export function useUrlState<T extends JSONCompatible>(
);
}, [sp]);

const reset = React.useCallback(
(options?: Options & { [key: string]: unknown }) => {
resetBase({ ...defOpts, ...options });
},
[resetBase],
);

return {
/**
* * Example:
Expand Down Expand Up @@ -170,6 +190,17 @@ export function useUrlState<T extends JSONCompatible>(
* @deprecated use `urlState`
*/
state,
/**
* * Example:
* ```ts
* reset();
* // or
* reset({ replace: false, scroll: true })
* ```
*
* * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/next/useUrlState#reset}
*/
reset,
getState,
};
}
Expand Down
13 changes: 12 additions & 1 deletion packages/urlstate/react-router/useUrlState/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ A custom React hook that manages state and synchronizes it with URL search param

- `defaultState: object` - An object representing the default state values.
- `replace?: boolean` - Control will `setUrl` use `replace` or `push` methods on router, default replace=true, can override by `updateUrl(stateObj, { replace: false })`
- `options?: NavigateOptions` - type from NavigateOptions of `react-router` type, same as options from `useNavigate`
- `options?: NavigateOptions` - `replace` arg and types from `NavigateOptions` of `react-router` type, same as options from `useNavigate`
- `useHistory` - Optionally can use window.history for navigation
- `preventScrollReset` - Option from react-router navigate

### Returns:

Expand All @@ -20,6 +21,7 @@ An object containing:
- `urlState: object` - The current state.
- `setState: Function` - Function to update the state without updating the URL.
- `setUrl: Function` - Function to update both the state and the URL.
- `reset: Function` - Function to reset state to default.

### Example:

Expand Down Expand Up @@ -56,3 +58,12 @@ Updates both the state and the URL.

- `value?: T | Partial<T> | (currState: T) => T` - Optional new state value or a function that receives the current state and returns the new state.
- `options?: NavigateOptions` - Optional options object from react-router's `NavigateOptions` type.

## `reset`

Updates both the state and the URL.

### Parameters:

- `options?: NavigateOptions` - Optional options object from react-router's `NavigateOptions` type.

44 changes: 38 additions & 6 deletions packages/urlstate/react-router/useUrlState/useUrlState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,14 @@ export function useUrlState<T extends JSONCompatible>({
* @param {JSONCompatible<T>} [defaultState] Fallback (default) values for state
* @param {Object} params - Object with other parameters
* @param {NavigateOptions} params.NavigateOptions See type from `react-router-dom`
* @param {boolean} params.replace replace URL of push, default `true`
* @param {boolean} params.replace replace URL or push, default `true`
* @param {boolean} params.useHistory use window.history for navigation, default `false`
* @param {boolean} params.preventScrollReset keep scroll position, default `true`
* @returns {Object} [result] State and callbacks
* @returns {Object} [result.state] - current state object
* @returns {Function} [result.setUrl] - function to update state and url
* @returns {Function} [result.setState] - function to update state only
* @returns {Function} [result.reset] - function to reset state and url to default
* * Example:
* ```ts
* export const form = { name: '', age: 0 };
Expand All @@ -53,6 +58,10 @@ export function useUrlState<T extends JSONCompatible>({
* setUrl({ name: 'test' }, { replace: true });
* // similar to React.useState
* setUrl(curr => ({ ...curr, name: 'test' }), { replace: true });
* // reset state and url
* reset();
* reset({ replace: true });
* // same as setState(form) with setUrl(form)
* ```
*
* * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react-router/useUrlState#api}
Expand All @@ -67,6 +76,7 @@ export function useUrlState<T extends JSONCompatible>(
value?: Partial<T> | ((currState: T) => T),
options?: Params,
) => void;
reset: (options?: NavigateOptions & { [key: string]: unknown }) => void;
};

export function useUrlState<T extends JSONCompatible>(
Expand All @@ -92,32 +102,36 @@ export function useUrlState<T extends JSONCompatible>(
preventScrollReset: params?.preventScrollReset as boolean,
};

const defOpts = React.useMemo(() => ({ ...defaultOpts, ..._opts }), []);

const navigate = useNavigate();
const router = React.useMemo(
() =>
_useHistory
? routerHistory
: {
replace: (url: string, options: NavigateOptions) =>
navigate(url, { ...defaultOpts, ..._opts, ...options }),
navigate(url, { ...defOpts, ...options }),
push: (url: string, options: NavigateOptions) =>
navigate(url, { ...defaultOpts, ..._opts, ...options }),
navigate(url, { ...defOpts, ...options }),
},
[navigate, _opts],
[navigate],
);

const {
state,
updateState,
updateUrl: updateUrlBase,
getState,
reset: resetBase,
} = useUrlStateBase(_defaultState, router, ({ parse }) =>
parse(filterUnknownParamsClient(_defaultState)),
);

const updateUrl = React.useCallback(
(value?: Parameters<typeof updateUrlBase>[0], options?: NavigateOptions) =>
updateUrlBase(value, { ...defaultOpts, ..._opts, ...options }),
[_opts],
updateUrlBase(value, { ...defOpts, ...options }),
[updateUrlBase],
);

const [sp] = useSearchParams();
Expand All @@ -137,6 +151,13 @@ export function useUrlState<T extends JSONCompatible>(
);
}, [sp]);

const reset = React.useCallback(
(options?: NavigateOptions & { [key: string]: unknown }) => {
resetBase({ ...defOpts, ...options });
},
[resetBase],
);

return {
/**
* * Example:
Expand Down Expand Up @@ -180,6 +201,17 @@ export function useUrlState<T extends JSONCompatible>(
* @deprecated use `urlState`
*/
state,
/**
* * Example:
* ```ts
* reset();
* // or
* reset({ replace: false, preventScrollReset: false })
* ```
*
* * Docs {@link https://github.com/asmyshlyaev177/state-in-url/tree/master/packages/urlstate/react-router/useUrlState#reset}
*/
reset,
getState,
};
}
Expand Down
8 changes: 8 additions & 0 deletions packages/urlstate/useUrlStateBase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ An object containing:
- `getState: Function` - Function to get state.
- `updateState: Function` - Function to update the state without updating the URL.
- `updateUrl: Function` - Function to update both the state and the URL.
- `reset: Function` - Function to reset state and url to default values

### Example:

Expand Down Expand Up @@ -52,3 +53,10 @@ Updates both the state and the URL.
- `value?: T | Partial<T> | (currState: T) => T` - Optional new state value or a function that receives the current state and returns the new state.
- `options?: Options` - Optional options object. When `replace` is true it will use router.replace. Other nextjs native options for `router`'s push/replace.

## `reset`

Reset state and URL to default

### Parameters:

- `options?: Options` - Optional options object. When `replace` is true it will use router.replace. Other nextjs native options for `router`'s push/replace.
Loading

0 comments on commit 3f00560

Please sign in to comment.