-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8e7194d
commit 525cf48
Showing
6 changed files
with
153 additions
and
1 deletion.
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 { atomWithQueryParams } from 'jotai-location'; | ||
import { useAtom } from 'jotai/react'; | ||
import React from 'react'; | ||
|
||
const pageAtom = atomWithQueryParams('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,85 @@ | ||
import type { SetStateAction, WritableAtom } from 'jotai'; | ||
import { atom } from 'jotai'; | ||
import { atomWithLocation } from 'jotai-location'; | ||
|
||
// Create an atom for managing location state, including query parameters. | ||
const locationAtom = atomWithLocation(); | ||
|
||
/** | ||
* Creates a writable Jotai atom to manage a query parameter in the URL. | ||
* | ||
* @template T - The type of the query parameter value (e.g., string or number). | ||
* @param key - The name of the query parameter to manage. | ||
* @param defaultValue - The default value for the query parameter if not present in the URL. | ||
* @returns A writable atom for reading and updating the query parameter. | ||
*/ | ||
export const atomWithQueryParams = <T>( | ||
key: string, | ||
defaultValue: T, | ||
): WritableAtom<T, [SetStateAction<T>], void> => { | ||
/** | ||
* Resolves the value of a query parameter based on its type. | ||
* | ||
* @param value - The raw string value from the URL (or `null` if absent). | ||
* @returns The resolved value cast to type `T`. | ||
*/ | ||
const resolveDefaultValue = (value: string | null | undefined): T => { | ||
// If the value is null or undefined, return the default value. | ||
if (value === null || value === undefined) { | ||
return defaultValue; | ||
} | ||
|
||
// If the default value is a number, attempt to parse the value as a number. | ||
if (typeof defaultValue === 'number') { | ||
const parsed = Number(value); | ||
return (isNaN(parsed) ? defaultValue : parsed) as T; | ||
} | ||
|
||
// Otherwise, return the value as a string (or other compatible type). | ||
return value as T; | ||
}; | ||
|
||
return atom<T, [SetStateAction<T>], void>( | ||
// Read function: retrieves the current value of the query parameter. | ||
(get) => { | ||
const searchParams = get(locationAtom).searchParams; | ||
const paramValue = searchParams?.get(key); | ||
|
||
// Use the resolver function to handle type casting and defaults. | ||
return resolveDefaultValue(paramValue); | ||
}, | ||
// Write function: updates the query parameter in the URL. | ||
(_, set, value) => { | ||
set(locationAtom, (prev) => { | ||
// Clone the current search parameters to avoid mutating the original object. | ||
const newSearchParams = new URLSearchParams(prev.searchParams); | ||
const currentValue = newSearchParams.get(key); | ||
|
||
let nextValue: string; | ||
|
||
if (typeof value === 'function') { | ||
// If the new value is a function, compute it based on the current value. | ||
const resolvedDefault = resolveDefaultValue(currentValue); | ||
|
||
nextValue = String( | ||
(value as (prev: T | undefined) => T)(resolvedDefault), | ||
); | ||
} else { | ||
// Otherwise, use the provided value directly. | ||
nextValue = String(value); | ||
} | ||
|
||
// Update the query parameter with the computed value. | ||
newSearchParams.set(key, nextValue); | ||
|
||
// Return a new location state with the updated query parameters. | ||
return { ...prev, searchParams: newSearchParams }; | ||
}); | ||
}, | ||
); | ||
}; | ||
|
||
// Usage | ||
// const pageAtom = atomWithQueryParams('page', 1); | ||
// const userIdAtom = atomWithQueryParams('userId', "1"); | ||
// const nameAtom = atomWithQueryParams<string>('name', "John"); |
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 { atomWithQueryParams } from './atomWithQueryParams'; |