Skip to content

Commit da305b2

Browse files
authored
feat: Improve global suspense-enabled data type (#4126)
* feat: add SWRGlobalConfig interface for suspense-enabled typing * examples: add global suspense-enabled example
1 parent 87cf235 commit da305b2

File tree

21 files changed

+335
-7
lines changed

21 files changed

+335
-7
lines changed

examples/suspense-global/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Basic
2+
3+
## One-Click Deploy
4+
5+
Deploy your own SWR project with Vercel.
6+
7+
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?s=https://github.com/vercel/swr/tree/main/examples/suspense)
8+
9+
## How to Use
10+
11+
Download the example:
12+
13+
```bash
14+
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/suspense
15+
cd suspense
16+
```
17+
18+
Install it and run:
19+
20+
```bash
21+
yarn
22+
yarn dev
23+
# or
24+
npm install
25+
npm run dev
26+
```
27+
28+
## The Idea behind the Example
29+
30+
Show how to use the SWR suspense option with React suspense.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react'
2+
3+
export default class ErrorBoundary extends React.Component<any> {
4+
state = { hasError: false, error: null }
5+
static getDerivedStateFromError(error: any) {
6+
return {
7+
hasError: true,
8+
error
9+
}
10+
}
11+
render() {
12+
if (this.state.hasError) {
13+
return this.props.fallback
14+
}
15+
return this.props.children
16+
}
17+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use client'
2+
3+
import { SWRConfig } from 'swr'
4+
5+
import fetcher from './libs/fetch'
6+
7+
declare module 'swr' {
8+
interface SWRGlobalConfig {
9+
suspense: true
10+
}
11+
}
12+
13+
export function GlobalSWRConfig({ children }: { children: React.ReactNode }) {
14+
return (
15+
<SWRConfig
16+
value={{
17+
fetcher,
18+
suspense: true
19+
}}
20+
>
21+
{children}
22+
</SWRConfig>
23+
)
24+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default async function fetcher(...args: [any]) {
2+
const res = await fetch(...args)
3+
if (!res.ok) {
4+
throw new Error('An error occurred while fetching the data.')
5+
} else {
6+
return res.json()
7+
}
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/// <reference types="next" />
2+
/// <reference types="next/image-types/global" />
3+
4+
// NOTE: This file should not be edited
5+
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "suspense-global",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"dependencies": {
7+
"next": "latest",
8+
"react": "latest",
9+
"react-dom": "latest",
10+
"swr": "latest"
11+
},
12+
"scripts": {
13+
"dev": "next",
14+
"start": "next start",
15+
"build": "next build"
16+
}
17+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import dynamic from 'next/dynamic'
2+
import Link from 'next/link'
3+
import { Suspense } from 'react'
4+
import ErrorHandling from '../../components/error-handling'
5+
import { useRouter } from 'next/router'
6+
7+
const Detail = dynamic(() => import('./detail'), {
8+
ssr: false
9+
})
10+
11+
export default function Repo() {
12+
const router = useRouter()
13+
if (!router.isReady) return null
14+
const { user, repo } = router.query
15+
const id = `${user}/${repo}`
16+
17+
return (
18+
<div style={{ textAlign: 'center' }}>
19+
<h1>{id}</h1>
20+
<Suspense fallback={<div>loading...</div>}>
21+
<ErrorHandling fallback={<div>oooops!</div>}>
22+
<Detail id={id}></Detail>
23+
</ErrorHandling>
24+
</Suspense>
25+
<br />
26+
<br />
27+
<Link href="/">Back</Link>
28+
</div>
29+
)
30+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import useSWR from 'swr'
2+
import { RepoData } from '../api/data'
3+
4+
const Detail = ({ id }: { id: string }) => {
5+
const { data } = useSWR<RepoData>('/api/data?id=' + id)
6+
7+
return (
8+
<>
9+
{data ? (
10+
<div>
11+
<p>forks: {data.forks_count}</p>
12+
<p>stars: {data.stargazers_count}</p>
13+
<p>watchers: {data.watchers}</p>
14+
</div>
15+
) : null}
16+
</>
17+
)
18+
}
19+
20+
export default Detail
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { AppProps } from 'next/app'
2+
import { GlobalSWRConfig } from 'global-swr-config'
3+
4+
export default function MyApp({ Component, pageProps }: AppProps) {
5+
return (
6+
<GlobalSWRConfig>
7+
<Component {...pageProps} />
8+
</GlobalSWRConfig>
9+
)
10+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { NextApiRequest, NextApiResponse } from 'next'
2+
3+
const projects = [
4+
'facebook/flipper',
5+
'vuejs/vuepress',
6+
'rust-lang/rust',
7+
'vercel/next.js',
8+
'emperor/clothes'
9+
] as const
10+
11+
export type ProjectsData = typeof projects
12+
13+
export interface RepoData {
14+
forks_count: number
15+
stargazers_count: number
16+
watchers: number
17+
}
18+
19+
export default function api(req: NextApiRequest, res: NextApiResponse) {
20+
if (req.query.id) {
21+
if (req.query.id === projects[4]) {
22+
setTimeout(() => {
23+
res.status(404).json({ msg: 'not found' })
24+
})
25+
} else {
26+
// a slow endpoint for getting repo data
27+
fetch(`https://api.github.com/repos/${req.query.id}`)
28+
.then(res => res.json())
29+
.then(data => {
30+
setTimeout(() => {
31+
res.json(data)
32+
}, 2000)
33+
})
34+
}
35+
} else {
36+
setTimeout(() => {
37+
res.json(projects)
38+
}, 2000)
39+
}
40+
}

0 commit comments

Comments
 (0)