Skip to content

Commit 062de50

Browse files
authored
Merge branch 'master' into fix-sync-cache
2 parents 1d26c95 + 66aaefd commit 062de50

32 files changed

+1043
-305
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
dist/**/*
22
esm/**/*
33
node_modules
4+
playground

.eslintrc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
"plugin:@typescript-eslint/eslint-recommended",
1717
"plugin:@typescript-eslint/recommended",
1818
"prettier",
19-
"prettier/@typescript-eslint"
19+
"prettier/@typescript-eslint",
20+
"plugin:jest-dom/recommended",
21+
"plugin:testing-library/react"
2022
],
2123
"settings": {
2224
"react": {

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ esm
1010
.vscode
1111
examples/**/yarn.lock
1212
package-lock.json
13+
playground/**

CONTRIBUTING.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# SWR Contribution Guidelines
2+
3+
Thank you for reading this guide and we appreciate any contribution.
4+
5+
## Ask a Question
6+
7+
You can use the repository's [Discussions](https://github.com/vercel/swr/discussions) page to ask any questions, post feedback or share your experience on how you use this library.
8+
9+
## Report a Bug
10+
11+
Whenever you find something which is not working properly, please first search the repository's [Issues](https://github.com/vercel/swr/issues) page and make sure it's not reported by someone else already.
12+
13+
If not, feel free to open an issue with the detailed description of the problem and the expected behavior. And reproduction (for example a [CodeSandbox](https://codesandbox.io) link) will be extremely helpful.
14+
15+
## Request for a New Feature
16+
17+
For new features, it would be great to have some discussions from the community before starting working on it. You can either create an issue (if there isn't one) or post a thread in the [Discussions](https://github.com/vercel/swr/discussions) page to describe the feature that you want to have.
18+
19+
If possible, you can add other additional context like how this feature can be implemented technically, what other alternative solutions we can have, etc.
20+
21+
## Open a PR for Bugfix or Feature
22+
23+
### Local Development
24+
25+
To develop SWR locally, you can use the Vite SWR playground to play with the source code inside the browser. You can follow these steps:
26+
27+
```bash
28+
yarn install
29+
yarn register
30+
yarn build
31+
yarn prepare:vite
32+
yarn dev:vite
33+
```
34+
35+
To test SSR related features, you need to use the Next.js SWR playground instead:
36+
37+
```bash
38+
yarn install
39+
yarn register
40+
yarn build
41+
yarn prepare:next
42+
yarn dev:next
43+
```
44+
45+
## Update Documentation
46+
47+
To update the [SWR Documentation](https://swr.vercel.app), you can contribute to the [website repository](https://github.com/vercel/swr-site).

README.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -347,11 +347,15 @@ function Profile() {
347347
<h1>My name is {data.name}.</h1>
348348
<button onClick={async () => {
349349
const newName = data.name.toUpperCase()
350-
// send a request to the API to update the data
350+
351+
// update the local data immediately, but disable the revalidation
352+
mutate('/api/user', { ...data, name: newName }, false)
353+
354+
// send a request to the API to update the source
351355
await requestUpdateUsername(newName)
352-
// update the local data immediately and revalidate (refetch)
353-
// NOTE: key has to be passed to mutate as it's not bound
354-
mutate('/api/user', { ...data, name: newName })
356+
357+
// trigger a revalidation (refetch) to make sure our local data is correct
358+
mutate('/api/user')
355359
}}>Uppercase my name!</button>
356360
</div>
357361
)

examples/infinite-scroll/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# useSWRInfinite with scroll based on IntersectionObserver
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/project?template=https://github.com/vercel/swr/tree/master/examples/infinite-scroll)
8+
9+
## How to Use
10+
11+
Download the example:
12+
13+
```bash
14+
curl https://codeload.github.com/vercel/swr/tar.gz/master | tar -xz --strip=2 swr-master/examples/infinite-scroll
15+
cd basic
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+
Deploy it to the cloud with [now](https://vercel.com/home) ([download](https://vercel.com/download))
29+
30+
```
31+
now
32+
```
33+
34+
## The Idea behind the Example
35+
36+
Show usage of useSWRInfinite with infinite scroll based on IntersectionObserver
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { useState, useEffect } from 'react'
2+
3+
const useOnScreen = (ref) => {
4+
const [isIntersecting, setIntersecting] = useState(false)
5+
6+
useEffect(() => {
7+
const observer = new IntersectionObserver(([entry]) =>
8+
setIntersecting(entry.isIntersecting)
9+
)
10+
11+
observer.observe(ref.current)
12+
// Remove the observer as soon as the component is unmounted
13+
return () => {
14+
observer.disconnect()
15+
}
16+
}, [])
17+
18+
return isIntersecting
19+
}
20+
21+
export default useOnScreen
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import fetch from 'isomorphic-unfetch'
2+
3+
export default async function (...args) {
4+
const res = await fetch(...args)
5+
return res.json()
6+
}

examples/infinite-scroll/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "infinite-scroll",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"dependencies": {
7+
"isomorphic-unfetch": "3.0.0",
8+
"next": "10.0.6",
9+
"react": "17.0.1",
10+
"react-dom": "17.0.1",
11+
"swr": "latest"
12+
},
13+
"scripts": {
14+
"dev": "next",
15+
"start": "next start",
16+
"build": "next build"
17+
}
18+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { useSWRInfinite } from 'swr'
2+
import { useState, useRef, useEffect } from 'react'
3+
4+
import fetcher from '../libs/fetch'
5+
import useOnScreen from '../hooks/useOnScreen'
6+
7+
const PAGE_SIZE = 6
8+
9+
const getKey = (pageIndex, previousPageData, repo, pageSize) => {
10+
if (previousPageData && !previousPageData.length) return null // reached the end
11+
12+
return `https://api.github.com/repos/${repo}/issues?per_page=${pageSize}&page=${
13+
pageIndex + 1
14+
}`
15+
}
16+
17+
export default function App() {
18+
const ref = useRef()
19+
const [repo, setRepo] = useState('facebook/react')
20+
const [val, setVal] = useState(repo)
21+
22+
const isVisible = useOnScreen(ref)
23+
24+
const { data, error, mutate, size, setSize, isValidating } = useSWRInfinite(
25+
(...args) => getKey(...args, repo, PAGE_SIZE),
26+
fetcher
27+
)
28+
29+
const issues = data ? [].concat(...data) : []
30+
const isLoadingInitialData = !data && !error
31+
const isLoadingMore =
32+
isLoadingInitialData ||
33+
(size > 0 && data && typeof data[size - 1] === 'undefined')
34+
const isEmpty = data?.[0]?.length === 0
35+
const isReachingEnd = size === PAGE_SIZE
36+
const isRefreshing = isValidating && data && data.length === size
37+
38+
useEffect(() => {
39+
if (isVisible && !isReachingEnd && !isRefreshing) {
40+
setSize(size + 1)
41+
}
42+
}, [isVisible, isRefreshing])
43+
44+
return (
45+
<div style={{ fontFamily: 'sans-serif' }}>
46+
<input
47+
value={val}
48+
onChange={(e) => setVal(e.target.value)}
49+
placeholder="facebook/reect"
50+
/>
51+
<button
52+
onClick={() => {
53+
setRepo(val)
54+
setSize(1)
55+
}}
56+
>
57+
load issues
58+
</button>
59+
<p>
60+
showing {size} page(s) of {isLoadingMore ? '...' : issues.length}{' '}
61+
issue(s){' '}
62+
<button disabled={isRefreshing} onClick={() => mutate()}>
63+
{isRefreshing ? 'refreshing...' : 'refresh'}
64+
</button>
65+
<button disabled={!size} onClick={() => setSize(0)}>
66+
clear
67+
</button>
68+
</p>
69+
{isEmpty ? <p>Yay, no issues found.</p> : null}
70+
{issues.map((issue) => {
71+
return (
72+
<p key={issue.id} style={{ margin: '6px 0', height: 50 }}>
73+
- {issue.title}
74+
</p>
75+
)
76+
})}
77+
<div ref={ref}>
78+
{isLoadingMore ? 'loading...' : isReachingEnd ? 'no more issues' : ''}
79+
</div>
80+
</div>
81+
)
82+
}

examples/infinite/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# useSWRInfinite
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/project?template=https://github.com/vercel/swr/tree/master/examples/infinite)
8+
9+
## How to Use
10+
11+
Download the example:
12+
13+
```bash
14+
curl https://codeload.github.com/vercel/swr/tar.gz/master | tar -xz --strip=2 swr-master/examples/infinite
15+
cd basic
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+
Deploy it to the cloud with [now](https://vercel.com/home) ([download](https://vercel.com/download))
29+
30+
```
31+
now
32+
```
33+
34+
## The Idea behind the Example
35+
36+
Show usage of useSWRInfinite with load more data button

examples/infinite/libs/fetch.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import fetch from 'isomorphic-unfetch'
2+
3+
export default async function (...args) {
4+
const res = await fetch(...args)
5+
return res.json()
6+
}

examples/infinite/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "infinite",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"license": "MIT",
6+
"dependencies": {
7+
"isomorphic-unfetch": "3.0.0",
8+
"next": "10.0.6",
9+
"react": "17.0.1",
10+
"react-dom": "17.0.1",
11+
"swr": "latest"
12+
},
13+
"scripts": {
14+
"dev": "next",
15+
"start": "next start",
16+
"build": "next build"
17+
}
18+
}

examples/infinite/pages/index.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { useState } from 'react'
2+
import { useSWRInfinite } from 'swr'
3+
4+
import fetch from '../libs/fetch'
5+
6+
const PAGE_SIZE = 6
7+
8+
export default function App() {
9+
const [repo, setRepo] = useState('reactjs/react-a11y')
10+
const [val, setVal] = useState(repo)
11+
12+
const { data, error, mutate, size, setSize, isValidating } = useSWRInfinite(
13+
(index) =>
14+
`https://api.github.com/repos/${repo}/issues?per_page=${PAGE_SIZE}&page=${
15+
index + 1
16+
}`,
17+
fetch
18+
)
19+
20+
const issues = data ? [].concat(...data) : []
21+
const isLoadingInitialData = !data && !error
22+
const isLoadingMore =
23+
isLoadingInitialData ||
24+
(size > 0 && data && typeof data[size - 1] === 'undefined')
25+
const isEmpty = data?.[0]?.length === 0
26+
const isReachingEnd =
27+
isEmpty || (data && data[data.length - 1]?.length < PAGE_SIZE)
28+
const isRefreshing = isValidating && data && data.length === size
29+
30+
return (
31+
<div style={{ fontFamily: 'sans-serif' }}>
32+
<input
33+
value={val}
34+
onChange={(e) => setVal(e.target.value)}
35+
placeholder="reactjs/react-a11y"
36+
/>
37+
<button
38+
onClick={() => {
39+
setRepo(val)
40+
setSize(1)
41+
}}
42+
>
43+
load issues
44+
</button>
45+
<p>
46+
showing {size} page(s) of {isLoadingMore ? '...' : issues.length}{' '}
47+
issue(s){' '}
48+
<button
49+
disabled={isLoadingMore || isReachingEnd}
50+
onClick={() => setSize(size + 1)}
51+
>
52+
{isLoadingMore
53+
? 'loading...'
54+
: isReachingEnd
55+
? 'no more issues'
56+
: 'load more'}
57+
</button>
58+
<button disabled={isRefreshing} onClick={() => mutate()}>
59+
{isRefreshing ? 'refreshing...' : 'refresh'}
60+
</button>
61+
<button disabled={!size} onClick={() => setSize(0)}>
62+
clear
63+
</button>
64+
</p>
65+
{isEmpty ? <p>Yay, no issues found.</p> : null}
66+
{issues.map((issue) => {
67+
return (
68+
<p key={issue.id} style={{ margin: '6px 0' }}>
69+
- {issue.title}
70+
</p>
71+
)
72+
})}
73+
</div>
74+
)
75+
}

0 commit comments

Comments
 (0)