Skip to content

Commit b350556

Browse files
chore: add example for Error Boundary with Jotai and TanStack Query (#129)
1 parent dedca93 commit b350556

File tree

11 files changed

+227
-2
lines changed

11 files changed

+227
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ All atoms can be used within the context of a server side rendered app, such as
441441
Fetch error will be thrown and can be caught with ErrorBoundary.
442442
Refetching may recover from a temporary error.
443443

444-
See [a working example](https://codesandbox.io/s/4gfp6z) to learn more.
444+
See [a working example](https://stackblitz.com/github/jotaijs/jotai-tanstack-query/tree/main/examples/09_error_boundary) to learn more.
445445

446446
### Devtools
447447

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>jotai-tanstack-query example</title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<script type="module" src="/src/index.tsx"></script>
11+
</body>
12+
</html>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "jotai-tanstack-query-example-error-boundary",
3+
"version": "0.1.0",
4+
"private": true,
5+
"dependencies": {
6+
"@tanstack/query-core": "latest",
7+
"jotai": "latest",
8+
"jotai-tanstack-query": "*",
9+
"react": "latest",
10+
"react-dom": "latest",
11+
"react-error-boundary": "latest"
12+
},
13+
"devDependencies": {
14+
"@types/react": "latest",
15+
"@types/react-dom": "latest",
16+
"@vitejs/plugin-react": "latest",
17+
"typescript": "latest",
18+
"vite": "latest"
19+
},
20+
"scripts": {
21+
"dev": "vite",
22+
"build": "vite build",
23+
"preview": "vite preview"
24+
}
25+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { Suspense } from 'react'
2+
import { atom, useAtom, useSetAtom } from 'jotai'
3+
import { atomWithSuspenseQuery } from 'jotai-tanstack-query'
4+
import { ErrorBoundary, type FallbackProps } from 'react-error-boundary'
5+
6+
const idAtom = atom(1)
7+
const userAtom = atomWithSuspenseQuery<User>((get) => ({
8+
queryKey: ['user', get(idAtom)],
9+
queryFn: async ({ queryKey: [, id] }) => {
10+
const randomNumber = Math.floor(Math.random() * 10)
11+
if (randomNumber % 3 === 0) {
12+
await fetch(`https://jsonplaceholder.typicode.com/users/error`)
13+
return await Promise.reject('fetch failed')
14+
}
15+
16+
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
17+
return res.json()
18+
},
19+
retry: false,
20+
}))
21+
22+
const UserData = () => {
23+
const [{ data }] = useAtom(userAtom)
24+
25+
return (
26+
<>
27+
<UserDisplay user={data} />
28+
</>
29+
)
30+
}
31+
32+
interface User {
33+
id: number
34+
name: string
35+
email: string
36+
}
37+
38+
const UserDisplay = ({ user }: { user: User }) => {
39+
return (
40+
<div>
41+
<div>ID: {user.id}</div>
42+
<strong>{user.name}</strong> - {user.email}
43+
</div>
44+
)
45+
}
46+
47+
const Controls = () => {
48+
const [id, setId] = useAtom(idAtom)
49+
return (
50+
<>
51+
<div>
52+
ID: {id}{' '}
53+
<button type="button" onClick={() => setId((c) => c - 1)}>
54+
Prev
55+
</button>{' '}
56+
<button type="button" onClick={() => setId((c) => c + 1)}>
57+
Next
58+
</button>
59+
</div>
60+
</>
61+
)
62+
}
63+
64+
const Fallback = ({ error, resetErrorBoundary }: FallbackProps) => {
65+
const reset = useSetAtom(userAtom)
66+
const retry = () => {
67+
reset()
68+
resetErrorBoundary()
69+
}
70+
return (
71+
<div role="alert">
72+
<p>Something went wrong:</p>
73+
<pre>{error.message}</pre>
74+
<button onClick={retry}>Try again</button>
75+
</div>
76+
)
77+
}
78+
79+
const App = () => {
80+
return (
81+
<ErrorBoundary FallbackComponent={Fallback}>
82+
<Suspense fallback="Loading...">
83+
<Controls />
84+
<UserData />
85+
</Suspense>
86+
</ErrorBoundary>
87+
)
88+
}
89+
90+
export default App
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react'
2+
import { createRoot } from 'react-dom/client'
3+
import App from './App'
4+
5+
const ele = document.getElementById('app')
6+
if (ele) {
7+
createRoot(ele).render(React.createElement(App))
8+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"useDefineForClassFields": true,
5+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
6+
"module": "ESNext",
7+
"skipLibCheck": true,
8+
9+
/* Bundler mode */
10+
"moduleResolution": "bundler",
11+
"allowImportingTsExtensions": true,
12+
"resolveJsonModule": true,
13+
"isolatedModules": true,
14+
"noEmit": true,
15+
"jsx": "react-jsx",
16+
17+
/* Linting */
18+
"strict": true,
19+
"noUnusedLocals": true,
20+
"noUnusedParameters": true,
21+
"noFallthroughCasesInSwitch": true
22+
},
23+
"include": ["src"],
24+
"references": [{ "path": "./tsconfig.node.json" }]
25+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"composite": true,
4+
"skipLibCheck": true,
5+
"module": "ESNext",
6+
"moduleResolution": "bundler",
7+
"allowSyntheticDefaultImports": true
8+
},
9+
"include": ["vite.config.ts"]
10+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import react from '@vitejs/plugin-react'
2+
import { defineConfig } from 'vite'
3+
4+
export default defineConfig({
5+
plugins: [react()],
6+
})

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@
5151
"examples:05_mutation": "pnpm --filter ./examples/05_mutation run dev",
5252
"examples:06_refetch": "pnpm --filter ./examples/06_refetch run dev",
5353
"examples:07_queries": "pnpm --filter ./examples/07_queries run dev",
54-
"examples:08_incremental_adoption": "pnpm --filter ./examples/08_incremental_adoption run dev"
54+
"examples:08_incremental_adoption": "pnpm --filter ./examples/08_incremental_adoption run dev",
55+
"examples:09_error_boundary": "pnpm --filter ./examples/09_error_boundary run dev"
5556
},
5657
"keywords": [
5758
"jotai",

pnpm-lock.yaml

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)