-
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
Open
jiangtaste
wants to merge
12
commits into
jotaijs:main
Choose a base branch
from
jiangtaste:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+219
−1
Open
Changes from 9 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
525cf48
add support for Query Params
jiangtaste 54e53e8
add support for Query Params
jiangtaste 50d76bc
fix typo
paul-hiyak a63a2bc
更新 atomWithQueryParams.ts
jiangtaste f83ef17
Use object destructuring
paul-hiyak 46a8647
Update src/index.ts
jiangtaste 2f7e58a
Update and rename atomWithQueryParams.ts to atomWithSearchParams.ts
jiangtaste 0c85ece
rename atomWithQueryParams to atomWithSearchParams
jiangtaste 3552972
Add functionality for formatting and parsing search parameters using …
paul-hiyak ff39fc9
Return the default value if it's not true or false.
paul-hiyak 9364a21
feat: enhance runtime type validation for atomWithSearchParams
jiangtaste fe90f72
fix: validate input type for resolveValue to ensure it is a string
jiangtaste File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "jotai-location-example", | ||
"version": "0.1.0", | ||
"private": true, | ||
"dependencies": { | ||
"@types/react": "latest", | ||
"@types/react-dom": "latest", | ||
"jotai": "latest", | ||
"jotai-location": "latest", | ||
"react": "latest", | ||
"react-dom": "latest", | ||
"react-scripts": "latest", | ||
"typescript": "latest" | ||
}, | ||
"scripts": { | ||
"start": "react-scripts start", | ||
"build": "react-scripts build", | ||
"test": "react-scripts test", | ||
"eject": "react-scripts eject" | ||
}, | ||
"browserslist": [ | ||
">0.2%", | ||
"not dead", | ||
"not ie <= 11", | ||
"not op_mini all" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<html> | ||
<head> | ||
<title>jotai-location example</title> | ||
</head> | ||
<body> | ||
<div id="app"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { atomWithSearchParams } from 'jotai-location'; | ||
import { useAtom } from 'jotai/react'; | ||
import React from 'react'; | ||
|
||
const pageAtom = atomWithSearchParams('page', 1); | ||
|
||
const Page = () => { | ||
const [page, setPage] = useAtom(pageAtom); | ||
return ( | ||
<div> | ||
<div>Page {page}</div> | ||
<button type="button" onClick={() => setPage((c) => c + 1)}> | ||
+1 | ||
</button> | ||
<p>See the url hash, change it there</p> | ||
</div> | ||
); | ||
}; | ||
|
||
const App = () => <Page />; | ||
|
||
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import React from 'react'; | ||
import { createRoot } from 'react-dom/client'; | ||
|
||
import App from './App'; | ||
|
||
const ele = document.getElementById('app'); | ||
if (ele) { | ||
createRoot(ele).render(<App />); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import type { SetStateAction, WritableAtom } from 'jotai/vanilla'; | ||
import { atom } from 'jotai/vanilla'; | ||
import { atomWithLocation } from './atomWithLocation'; | ||
|
||
// Create an atom for managing location state, including search parameters. | ||
const locationAtom = atomWithLocation(); | ||
|
||
/** | ||
* Creates an atom that manages a single search parameter. | ||
* | ||
* The atom automatically infers the type of the search parameter based on the | ||
* type of `defaultValue`. | ||
* | ||
* The atom's read function returns the current value of the search parameter. | ||
* The atom's write function updates the search parameter in the URL. | ||
* | ||
* @param key - The key of the search parameter. | ||
* @param defaultValue - The default value of the search parameter. | ||
* @returns A writable atom that manages the search parameter. | ||
*/ | ||
export const atomWithSearchParams = <T>( | ||
key: string, | ||
defaultValue: T, | ||
): WritableAtom<T, [SetStateAction<T>], void> => { | ||
/** | ||
* Resolves the value of a search parameter based on the type of `defaultValue`. | ||
* | ||
* @param value - The raw value from the URL (could be `null` or `undefined`). | ||
* @returns The resolved value matching the type of `defaultValue`. | ||
*/ | ||
const resolveValue = (value: string | null | undefined): T => { | ||
// If the value is null or undefined, return the default value. | ||
if (value === null || value === undefined) { | ||
return defaultValue; | ||
} | ||
|
||
// Determine the type of the default value and parse accordingly. | ||
if (typeof defaultValue === 'number') { | ||
return Number(value) as T; | ||
jiangtaste marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if (typeof defaultValue === 'boolean') { | ||
return (value === 'true') as T; | ||
jiangtaste marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
if (typeof defaultValue === 'string') { | ||
return value as T; | ||
jiangtaste marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// If the default value is an object, try to parse it as JSON. | ||
return JSON.parse(value) as T; | ||
jiangtaste marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
|
||
const parseValue = (value: T): string => { | ||
if ( | ||
typeof value !== 'number' && | ||
typeof value !== 'boolean' && | ||
typeof value !== 'string' | ||
) { | ||
// If the value is not a basic type, try to stringify it as JSON. | ||
return JSON.stringify(value); | ||
} | ||
return String(value); | ||
}; | ||
|
||
return atom<T, [SetStateAction<T>], void>( | ||
// Read function: Retrieves the current value of the search parameter. | ||
(get) => { | ||
const { searchParams } = get(locationAtom); | ||
|
||
// Resolve the value using the parsing logic. | ||
return resolveValue(searchParams?.get(key)); | ||
}, | ||
// Write function: Updates the search parameter in the URL. | ||
(_, set, value) => { | ||
set(locationAtom, (prev) => { | ||
// Create a new instance of URLSearchParams to avoid mutating the original. | ||
const newSearchParams = new URLSearchParams(prev.searchParams); | ||
|
||
let nextValue; | ||
|
||
if (typeof value === 'function') { | ||
// If the new value is a function, compute it based on the current value. | ||
const currentValue = resolveValue(newSearchParams.get(key)); | ||
nextValue = (value as (curr: T) => T)(currentValue); | ||
} else { | ||
// Otherwise, use the provided value directly. | ||
nextValue = value; | ||
} | ||
|
||
// Update the search parameter with the computed value. | ||
newSearchParams.set(key, parseValue(nextValue)); | ||
|
||
// Return the updated location state with new search parameters. | ||
return { ...prev, searchParams: newSearchParams }; | ||
}); | ||
}, | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { atomWithLocation } from './atomWithLocation'; | ||
export { atomWithHash } from './atomWithHash'; | ||
export { atomWithLocation } from './atomWithLocation'; | ||
export { atomWithSearchParams } from './atomWithSearchParams'; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
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)
oratomWithSearchParams("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.