-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for Search Params #39
base: main
Are you sure you want to change the base?
Conversation
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
Can you add a description about motivation, use cases, implementation approach, and so forth? |
This comment was marked as duplicate.
This comment was marked as duplicate.
Re: #39 (comment) Thanks. Can you also put them in the first commend of this PR? My little concern is that the implementation is on top of I think to avoid confusion with #20, we should probably rename |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your contribution! It looks good overall.
I’ve added them to the first comment of the PR. Renaming atomWithQueryParams to atomWithSearchParams avoids confusion with #20 and keeps things clear. Thanks! |
* @param defaultValue - The default value of the search parameter. | ||
* @returns A writable atom that manages the search parameter. | ||
*/ | ||
export const atomWithSearchParams = <T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, then limit only to support string, number and boolean.
export const atomWithSearchParams = <T>( | |
export const atomWithSearchParams = <T extends string | number | boolean>( |
and simply the code please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've tried, but I can't figure out why this strange type is being returned in TypeScript. I haven't yet pinpointed the reason.
Atom:
const pageAtom = atomWithSearchParams("page", 1)
Expected:
const pageAtom: WritableAtom<number, [SetStateAction<number>], void>
Got:
const pageAtom: WritableAtom<1, [SetStateAction<1>], void>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this case you need to do atomWithSearchParams<number>("page", 1)
or atomWithSearchParams("page", 1 as number)
. Do you have any other issues with this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, union type doesn't work. We should overload the function.
enhance runtime type validation for atomWithSearchParams
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We decided to support only primitive values. For other values, TS shouldn't accept it, and JS should raise an error.
* @param defaultValue - The default value of the search parameter. | ||
* @returns A writable atom that manages the search parameter. | ||
*/ | ||
export const atomWithSearchParams = <T>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this case you need to do atomWithSearchParams<number>("page", 1)
or atomWithSearchParams("page", 1 as number)
. Do you have any other issues with this change?
*/ | ||
const resolveValue = (value: string | null | undefined): T => { | ||
// If the value is null, undefined, or not a string, return the default value. | ||
if (value === null || value === undefined || typeof value !== 'string') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a preference, but let's do this:
if (value === null || value === undefined || typeof value !== 'string') { | |
if (typeof value !== 'string') { |
return defaultValue; | ||
} | ||
|
||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need try
?
// Determine the type of the default value and parse accordingly. | ||
if (typeof defaultValue === 'number') { | ||
if (value === '') { | ||
console.warn( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can happen. So, at least it should be DEV-only warning.
return parsed as T; | ||
} | ||
|
||
console.warn(`Expected a number for key "${key}", got "${value}".`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for this.
// Handle object types (assume JSON if defaultValue is an object) | ||
if (typeof defaultValue === 'object') { | ||
const parsed = JSON.parse(value); | ||
if (parsed && typeof parsed === 'object') { | ||
return parsed as T; | ||
} | ||
console.warn(`Expected an object for key "${key}", got "${value}".`); | ||
return defaultValue; | ||
} | ||
} catch (err) { | ||
console.error(`Failed to resolve value for key "${key}":`, err); | ||
return defaultValue; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's remove handling object. It's not type safe.
// Fallback to default value for unsupported types | ||
console.warn(`Unsupported defaultValue type for key "${key}".`); | ||
return defaultValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's throw an error. We shouldn't reach here.
Motivation:
Recoil is no longer actively maintained, and I have recently transitioned to using Jotai. I needed a solution for URL persistence with query parameters similar to what recoil-sync provides, so I implemented this feature in Jotai.
Use cases:
I’ve included a simple example under the examples/05_search_params, which demonstrates how to use the new functionality:
This will result in a URL like:
?key=defaultValue
.Implementation approach:
The implementation builds on Jotai's
atomWithLocation
from the jotai-location library, combined with Jotai’s basic read-write derived atoms to manage the state and sync it with URL query parameters.