forked from pmndrs/jotai
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: jotai/query (powered by react-query) (pmndrs#248)
* wip: jotai/query with react-query * fix package json * re implement jotai query * possible fix pending hack * fix: observer and pending * fix: copy script * chore: smplify code * chore: fix typo * query basic test * refetch query * refetch query test * typo * refactor atomWithQuery * fix failing test * chore: refactor * chore: simplify test * chore: refactor create pending * better react suspense * chore: precise types * wip: re-implement atomWithQuery * fix: initializing observe atom * fix: add optional peer dependency * update size snapshot * query loading * fix: making a test to fail * update docs * query loading 2 * new impl * new impl test * new typing * fix csb loading problems * new typing and a small fix * rename the type * reset pending on new fetch * some minor fixes * import type only from relative path * update react-query Co-authored-by: M. Bagher Abiat <[email protected]>
- Loading branch information
1 parent
23dbb02
commit df33993
Showing
10 changed files
with
465 additions
and
5 deletions.
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
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,38 @@ | ||
This doc describes `jotai/query` bundle. | ||
|
||
## Install | ||
|
||
You have to install `react-query` to access this bundle and its functions. | ||
|
||
``` | ||
yarn add react-query | ||
``` | ||
|
||
## atomWithQuery | ||
|
||
`atomWithQuery` creates a new atom with React Query. This function helps you use both atoms features and `useQuery` features in a single atom. | ||
|
||
```js | ||
import { useAtom } from 'jotai' | ||
import { atomWithQuery } from 'jotai/query' | ||
|
||
const idAtom = atom(1) | ||
const userAtom = atomWithQuery((get) => ({ | ||
queryKey: ['users', get(idAtom)], | ||
queryFn: async ({ queryKey: [, id] }) => { | ||
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`) | ||
return res.json() | ||
}, | ||
})) | ||
|
||
const UserData = () => { | ||
const [data] = useAtom(userAtom) | ||
return <div>{JSON.stringify(data)}</div> | ||
} | ||
``` | ||
|
||
### Examples | ||
|
||
Basic demo: [codesandbox](https://codesandbox.io/s/jotai-query-demo-ij2sd) | ||
|
||
Hackernews: [codesandbox](https://codesandbox.io/s/jotai-query-hacker-news-u4sli) |
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
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
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
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 @@ | ||
export { atomWithQuery } from './query/atomWithQuery' |
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,151 @@ | ||
import { | ||
QueryClient, | ||
QueryKey, | ||
QueryObserver, | ||
QueryObserverOptions, | ||
} from 'react-query' | ||
import { WritableAtom, atom } from 'jotai' | ||
import type { Getter, Setter } from '../core/types' | ||
|
||
type ResultActions = { type: 'refetch' } | ||
type AtomQueryOptions< | ||
TQueryFnData, | ||
TError, | ||
TData, | ||
TQueryData | ||
> = QueryObserverOptions<TQueryFnData, TError, TData, TQueryData> & { | ||
queryKey: QueryKey | ||
} | ||
|
||
const queryClientAtom = atom<QueryClient | null>(null) | ||
const getQueryClient = (get: Getter, set: Setter): QueryClient => { | ||
let queryClient = get(queryClientAtom) | ||
if (queryClient === null) { | ||
queryClient = new QueryClient() | ||
set(queryClientAtom, queryClient) | ||
} | ||
return queryClient | ||
} | ||
|
||
const createPending = <T>() => { | ||
const pending: { | ||
fulfilled: boolean | ||
promise?: Promise<T> | ||
resolve?: (data: T) => void | ||
} = { | ||
fulfilled: false, | ||
} | ||
pending.promise = new Promise<T>((resolve) => { | ||
pending.resolve = (data: T) => { | ||
resolve(data) | ||
pending.fulfilled = true | ||
} | ||
}) | ||
return pending as { | ||
fulfilled: boolean | ||
promise: Promise<T> | ||
resolve: (data: T) => void | ||
} | ||
} | ||
|
||
export function atomWithQuery< | ||
TQueryFnData, | ||
TError, | ||
TData = TQueryFnData, | ||
TQueryData = TQueryFnData | ||
>( | ||
createQuery: | ||
| AtomQueryOptions<TQueryFnData, TError, TData, TQueryData> | ||
| (( | ||
get: Getter | ||
) => AtomQueryOptions<TQueryFnData, TError, TData, TQueryData>) | ||
): WritableAtom<TData, ResultActions> { | ||
const pendingAtom = atom(createPending<TData>()) | ||
const dataAtom = atom<TData | null>(null) | ||
const queryAtom = atom< | ||
[ | ||
AtomQueryOptions<TQueryFnData, TError, TData, TQueryData>, | ||
WritableAtom<null, any> | ||
], | ||
ResultActions | ||
>( | ||
(get) => { | ||
const options = | ||
typeof createQuery === 'function' ? createQuery(get) : createQuery | ||
const observerAtom = atom( | ||
null, | ||
( | ||
get, | ||
set, | ||
action: | ||
| { type: 'init'; intializer: (queryClient: QueryClient) => void } | ||
| { type: 'data'; data: TData } | ||
) => { | ||
if (action.type === 'init') { | ||
const pending = get(pendingAtom) | ||
if (pending.fulfilled) { | ||
set(pendingAtom, createPending<TData>()) // new fetch | ||
} | ||
action.intializer(getQueryClient(get, set)) | ||
} else if (action.type === 'data') { | ||
set(dataAtom, action.data) | ||
const pending = get(pendingAtom) | ||
if (!pending.fulfilled) { | ||
pending.resolve(action.data) | ||
} | ||
} | ||
} | ||
) | ||
observerAtom.onMount = (dispatch) => { | ||
let unsub: (() => void) | undefined | false | ||
const intializer = (queryClient: QueryClient) => { | ||
const observer = new QueryObserver(queryClient, options) | ||
observer.subscribe((result) => { | ||
// TODO error handling | ||
if (result.data !== undefined) { | ||
dispatch({ type: 'data', data: result.data }) | ||
} | ||
}) | ||
if (unsub === false) { | ||
observer.destroy() | ||
} else { | ||
unsub = () => { | ||
observer.destroy() | ||
} | ||
} | ||
} | ||
dispatch({ type: 'init', intializer }) | ||
return () => { | ||
if (unsub) { | ||
unsub() | ||
} | ||
unsub = false | ||
} | ||
} | ||
return [options, observerAtom] | ||
}, | ||
async (get, set, action) => { | ||
if (action.type === 'refetch') { | ||
const [options] = get(queryAtom) | ||
set(pendingAtom, createPending<TData>()) // reset pending | ||
getQueryClient(get, set).getQueryCache().find(options.queryKey)?.reset() | ||
await getQueryClient(get, set).refetchQueries(options.queryKey) | ||
} | ||
} | ||
) | ||
const queryDataAtom = atom<TData, ResultActions>( | ||
(get) => { | ||
const [, observerAtom] = get(queryAtom) | ||
get(observerAtom) // use it here | ||
const data = get(dataAtom) | ||
const pending = get(pendingAtom) | ||
if (!pending.fulfilled) { | ||
return pending.promise | ||
} | ||
// we are sure that data is not null | ||
return data as TData | ||
}, | ||
(_get, set, action) => set(queryAtom, action) // delegate action | ||
) | ||
return queryDataAtom | ||
} |
Oops, something went wrong.