Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: translate "pagination" page #73

Merged
merged 3 commits into from
May 21, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 82 additions & 82 deletions pages/docs/pagination.ja.mdx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import Callout from 'nextra-theme-docs/callout'

# Pagination
# ページネーション

<Callout emoji="✅">
Please update to the latest version (≥ 0.3.0) to use this API. The previous <code>useSWRPages</code> API is now deprecated.
このAPIを使用するには、最新バージョン (≥ 0.3.0) に更新してください。以前の useSWRPages API は非推奨になりました。
</Callout>

SWR provides a dedicated API `useSWRInfinite` to support common UI patterns such as **pagination** and **infinite loading**.
SWR は、**ページネーション**や**無限ローディング**などの一般的な UI パターンをサポートするための専用 API である useSWRInfinite を提供しています。

## When to Use `useSWR`
## いつ `useSWR` を使用するか

### Pagination
### ページネーション

First of all, we might **NOT** need `useSWRInfinite` but can use just `useSWR` if we are building something like this:
まず第一に、`useSWRInfinite` は必要**ない**かもしれませんが、次のようなものを構築しようとするときには `useSWR` を使用できます:

<div className="mt-8" />

Expand All @@ -38,17 +38,17 @@ First of all, we might **NOT** need `useSWRInfinite` but can use just `useSWR` i
<path d="M239.349 327.119C241.13 327.119 242.438 326.054 242.434 324.605C242.438 323.501 241.769 322.709 240.61 322.53V322.462C241.522 322.227 242.114 321.511 242.11 320.531C242.114 319.249 241.062 318.153 239.383 318.153C237.781 318.153 236.494 319.121 236.451 320.54H237.725C237.755 319.739 238.509 319.253 239.366 319.253C240.256 319.253 240.84 319.794 240.836 320.599C240.84 321.443 240.163 321.997 239.195 321.997H238.458V323.071H239.195C240.406 323.071 241.104 323.685 241.104 324.562C241.104 325.411 240.367 325.986 239.34 325.986C238.394 325.986 237.657 325.5 237.606 324.724H236.268C236.323 326.148 237.585 327.119 239.349 327.119Z" fill="#454545"/>
</svg>

...which is a typical pagination UI. Let's see how it can be easily implemented with
`useSWR`:
...これは典型的なページネーション UI です。`useSWR` を使って簡単に実装する方法を
みてみましょう:

```jsx highlight=5
function App () {
const [pageIndex, setPageIndex] = useState(0);

// The API URL includes the page index, which is a React state.
// この API URL は、React の状態としてページのインデックスを含んでいます
const { data } = useSWR(`/api/data?page=${pageIndex}`, fetcher);

// ... handle loading and error states
// ... ローディングとエラー状態を処理します

return <div>
{data.map(item => <div key={item.id}>{item.name}</div>)}
Expand All @@ -58,13 +58,13 @@ function App () {
}
```

Furthermore, we can create an abstraction for this "page component":
さらに、この「ページコンポーネント」を抽象化できます:

```jsx highlight=13
function Page ({ index }) {
const { data } = useSWR(`/api/data?page=${index}`, fetcher);

// ... handle loading and error states
// ... ローディングとエラー状態を処理します

return data.map(item => <div key={item.id}>{item.name}</div>)
}
Expand All @@ -80,8 +80,8 @@ function App () {
}
```

Because of SWR's cache, we get the benefit to preload the next page. We render the next page inside
a hidden div, so SWR will trigger the data fetching of the next page. When the user navigates to the next page, the data is already there:
SWR のキャッシュがあるため、次のページを事前にロードできるという利点があります。次のページを非表示の div 内にレンダリングすると、
SWR は次のページのデータフェッチを開始します。ユーザーが次のページに移動したときには、データはすでにそこにあります。

```jsx highlight=6
function App () {
Expand All @@ -96,13 +96,13 @@ function App () {
}
```

With just 1 line of code, we get a much better UX. The `useSWR` hook is so powerful,
that most scenarios are covered by it.
たった 1 行のコードで、とても優れた UX を実現できます。`useSWR` フックは非常に強力で、
ほとんどのシナリオをカバーしています。

### Infinite Loading
### 無限ローディング

Sometimes we want to build an **infinite loading** UI, with a "Load More" button that appends data
to the list (or done automatically when you scroll):
「さらに読み込む」ボタンを使用して(またはスクロールすると自動的に実行されて)リストにデータを
追加する**無限ローディング** UI を構築したい場合があります:

<div className="mt-8" />

Expand All @@ -118,16 +118,16 @@ to the list (or done automatically when you scroll):
<path d="M354.49 309H359.761V307.866H355.807V300.273H354.49V309ZM363.918 309.132C365.763 309.132 366.969 307.781 366.969 305.757C366.969 303.72 365.763 302.369 363.918 302.369C362.073 302.369 360.867 303.72 360.867 305.757C360.867 307.781 362.073 309.132 363.918 309.132ZM363.923 308.062C362.717 308.062 362.154 307.01 362.154 305.753C362.154 304.5 362.717 303.435 363.923 303.435C365.12 303.435 365.683 304.5 365.683 305.753C365.683 307.01 365.12 308.062 363.923 308.062ZM370.297 309.145C371.379 309.145 371.988 308.595 372.231 308.105H372.282V309H373.527V304.653C373.527 302.749 372.027 302.369 370.987 302.369C369.802 302.369 368.711 302.847 368.285 304.04L369.483 304.312C369.67 303.848 370.147 303.401 371.004 303.401C371.826 303.401 372.248 303.831 372.248 304.572V304.602C372.248 305.067 371.771 305.058 370.595 305.195C369.355 305.339 368.085 305.663 368.085 307.151C368.085 308.438 369.052 309.145 370.297 309.145ZM370.574 308.122C369.853 308.122 369.333 307.798 369.333 307.168C369.333 306.486 369.939 306.243 370.676 306.145C371.089 306.089 372.069 305.979 372.252 305.795V306.639C372.252 307.415 371.635 308.122 370.574 308.122ZM377.674 309.128C378.867 309.128 379.336 308.399 379.566 307.982H379.673V309H380.917V300.273H379.643V303.516H379.566C379.336 303.111 378.901 302.369 377.683 302.369C376.102 302.369 374.938 303.618 374.938 305.74C374.938 307.858 376.085 309.128 377.674 309.128ZM377.955 308.041C376.817 308.041 376.225 307.04 376.225 305.727C376.225 304.428 376.805 303.452 377.955 303.452C379.067 303.452 379.664 304.359 379.664 305.727C379.664 307.104 379.055 308.041 377.955 308.041ZM386.013 300.273V309H387.266V302.68H387.347L389.921 308.987H390.961L393.535 302.685H393.616V309H394.869V300.273H393.271L390.492 307.057H390.39L387.612 300.273H386.013ZM399.438 309.132C401.283 309.132 402.489 307.781 402.489 305.757C402.489 303.72 401.283 302.369 399.438 302.369C397.593 302.369 396.387 303.72 396.387 305.757C396.387 307.781 397.593 309.132 399.438 309.132ZM399.442 308.062C398.236 308.062 397.674 307.01 397.674 305.753C397.674 304.5 398.236 303.435 399.442 303.435C400.64 303.435 401.202 304.5 401.202 305.753C401.202 307.01 400.64 308.062 399.442 308.062ZM403.911 309H405.185V305.003C405.185 304.146 405.846 303.528 406.749 303.528C407.013 303.528 407.312 303.575 407.414 303.605V302.386C407.286 302.369 407.035 302.357 406.873 302.357C406.106 302.357 405.45 302.791 405.211 303.494H405.143V302.455H403.911V309ZM411.103 309.132C412.531 309.132 413.541 308.429 413.831 307.364L412.625 307.146C412.395 307.764 411.841 308.08 411.116 308.08C410.025 308.08 409.292 307.372 409.258 306.111H413.912V305.659C413.912 303.294 412.497 302.369 411.014 302.369C409.19 302.369 407.988 303.759 407.988 305.77C407.988 307.803 409.173 309.132 411.103 309.132ZM409.262 305.156C409.314 304.227 409.987 303.422 411.022 303.422C412.011 303.422 412.659 304.155 412.663 305.156H409.262Z" fill="#454545"/>
</svg>

To implement this, we need to make **dynamic number of requests** on this page. React Hooks have [a couple of rules](https://reactjs.org/docs/hooks-rules.html),
so we **CANNOT** do something like this:
実装するには、このページで**動的な多くのリクエスト**を行う必要があります。
React フックには[いくつかのルール](https://reactjs.org/docs/hooks-rules.html)があるため、次のようなことは**できません**:

```jsx highlight=5,6,7,8,9
function App () {
const [cnt, setCnt] = useState(1)

const list = []
for (let i = 0; i < cnt; i++) {
// 🚨 This is wrong! Commonly, you can't use hooks inside a loop.
// 🚨 これは間違いです!通常、ループの中でフックは使えません
const { data } = useSWR(`/api/data?page=${i}`)
list.push(data)
}
Expand All @@ -137,12 +137,12 @@ function App () {
<div key={i}>{
data.map(item => <div key={item.id}>{item.name}</div>)
}</div>)}
<button onClick={() => setCnt(cnt + 1)}>Load More</button>
<button onClick={() => setCnt(cnt + 1)}>さらに読み込む</button>
</div>
}
```

Instead, we can use the `<Page />` abstraction that we created to achieve it:
代わりに、抽象化して作成した `<Page />` を使うことができます:

```jsx highlight=5,6,7
function App () {
Expand All @@ -155,18 +155,18 @@ function App () {

return <div>
{pages}
<button onClick={() => setCnt(cnt + 1)}>Load More</button>
<button onClick={() => setCnt(cnt + 1)}>さらに読み込む</button>
</div>
}
```

### Advanced Cases
### 高度なケース

However, in some advanced use cases, the solution above doesn't work.
しかし、一部の高度なユースケースでは、上記のソリューションが機能しません。

For example, we are still implementing the same "Load More" UI, but also need to display a number
about how many items are there in total. We can't use the `<Page />` solution anymore because
the top level UI (`<App />`) needs the data inside each page:
たとえば、先ほどの「さらに読み込む」UI をまだ実装しているときに、合計でいくつのアイテムがあるかの数値も
表示する必要がでてきました。トップレベルの UI (`<App />`) が各ページ内のデータを必要とするため、
`<Page />` を使ったソリューションは使えなくなってしまいました:

```jsx highlight=10
function App () {
Expand All @@ -180,58 +180,58 @@ function App () {
return <div>
<p>??? items</p>
{pages}
<button onClick={() => setCnt(cnt + 1)}>Load More</button>
<button onClick={() => setCnt(cnt + 1)}>さらに読み込む</button>
</div>
}
```

Also, if the pagination API is **cursor based**, that solution doesn't work either. Because each page
needs the data from the previous page, they're not isolated.
また、ページネーション API が**カーソルベース**の場合も、このソリューションは機能しません。
各ページには前ページのデータが必要なため、分離されていません。

That's how this new `useSWRInfinite` Hook can help.
ここで新しい `useSWRInfinite` フックが役立ちます。

## useSWRInfinite

`useSWRInfinite` gives us the ability to trigger a number of requests with one Hook. This is how it looks:
`useSWRInfinite` は、一つのフックで多数のリクエストを開始する機能を提供します。このような形になります:

```jsx
const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(
getKey, fetcher?, options?
)
```

Similar to `useSWR`, this new Hook accepts a function that returns the request key, a fetcher function, and options.
It returns all the values that `useSWR` returns, including 2 extra values: the page size and a page size setter, like a React state.
`useSWR` と同様に、この新しいフックは、リクエストキー、フェッチャー関数、およびオプションを返す関数を受け取ります。
これは `useSWR` が返すすべての値を返します。これらの値には、ページサイズと、React の状態のようなページサイズのセッターの二つの追加の値が含まれます。

In infinite loading, one _page_ is one request, and our goal is to fetch multiple pages and render them.
無限ローディングでは、1 _ページ_が一つのリクエストであり、目標は複数ページをフェッチしてレンダリングすることです。

### API

#### Parameters
#### 引数

- `getKey`: a function that accepts the index and the previous page data, returns the key of a page
- `fetcher`: same as `useSWR`'s [fetcher function](/docs/data-fetching)
- `options`: accepts all the options that `useSWR` supports, with 3 extra options:
- `initialSize = 1`: number of pages should be loaded initially
- `revalidateAll = false`: always try to revalidate all pages
- `persistSize = false`: don't reset the page size to 1 (or `initialSize` if set) when the first page's key changes
- `getKey`: インデックスと前ページのデータを受け取る関数であり、ページのキーを返します
- `fetcher`: `useSWR` の[フェッチャー関数](/docs/data-fetching)と同じ
- `options`: `useSWR` がサポートしているすべてのオプションに加えて、三つの追加オプションを受け取ります:
- `initialSize = 1`: 最初にロードするページ数
- `revalidateAll = false`: 常にすべてのページに対して再検証を試みる
- `persistSize = false`: 最初のページのキーが変更されたときに、ページサイズを 1 (またはセットされていれば `initialSize`)にリセットしない

<Callout>
Note that the `initialSize` option is not allowed to change in the lifecycle.
`initialSize` オプションはライフサイクルで変更できないことに注意してください。
</Callout>

#### Return Values
#### 返り値

- `data`: an array of fetch response values of each page
- `error`: same as `useSWR`'s `error`
- `isValidating`: same as `useSWR`'s `isValidating`
- `mutate`: same as `useSWR`'s bound mutate function but manipulates the data array
- `size`: the number of pages that _will_ be fetched and returned
- `setSize`: set the number of pages that need to be fetched
- `data`: 各ページのフェッチしたレスポンス値の配列
- `error`: `useSWR``error` と同じ
- `isValidating`: `useSWR``isValidating` と同じ
- `mutate`: `useSWR` のバインドされたミューテート関数と同じですが、データ配列を操作します
- `size`: フェッチして返される_だろう_ページ数
- `setSize`: フェッチする必要のあるページ数を設定します

### Example 1: Index Based Paginated API
### 例 1: インデックスにもとづいたページネーション API

For normal index based APIs:
通常のインデックスにもとづいた API の場合:

```
GET /users?page=0&limit=10
Expand All @@ -244,43 +244,43 @@ GET /users?page=0&limit=10
```

```jsx highlight=4,5,6,7,10
// A function to get the SWR key of each page,
// its return value will be accepted by `fetcher`.
// If `null` is returned, the request of that page won't start.
// 各ページの SWR キーを取得する関数であり、
// その返り値は `fetcher` に渡されます。
// `null` が返ってきた場合は、そのページのリクエストは開始されません。
const getKey = (pageIndex, previousPageData) => {
if (previousPageData && !previousPageData.length) return null // reached the end
return `/users?page=${pageIndex}&limit=10` // SWR key
if (previousPageData && !previousPageData.length) return null // 最後に到達した
return `/users?page=${pageIndex}&limit=10` // SWR キー
}

function App () {
const { data, size, setSize } = useSWRInfinite(getKey, fetcher)
if (!data) return 'loading'

// We can now calculate the number of all users
// これで、すべてのユーザー数を計算できます
let totalUsers = 0
for (let i = 0; i < data.length; i++) {
totalUsers += data[i].length
}

return <div>
<p>{totalUsers} users listed</p>
<p>{totalUsers} ユーザーがリストされています</p>
{data.map((users, index) => {
// `data` is an array of each page's API response.
// `data` は、各ページの API レスポンスの配列です
return users.map(user => <div key={user.id}>{user.name}</div>)
})}
<button onClick={() => setSize(size + 1)}>Load More</button>
<button onClick={() => setSize(size + 1)}>さらに読み込む</button>
</div>
}
```

The `getKey` function is the major difference between `useSWRInfinite` and `useSWR`.
It accepts the index of the current page, as well as the data from the previous page.
So both index based and cursor based pagination API can be supported nicely.
`getKey` 関数は、`useSWRInfinite` `useSWR` とで大きな違いがあります。
現在のページのインデックスに加えて、前のページのデータも受け入れます。
したがって、インデックスベースとカーソルベースの両方のページネーション API を適切にサポートできます。

Also `data` is no longer just one API response. It's an array of multiple API responses:
また、`data` は一つの API レスポンスだけではありません。複数の API レスポンスの配列になります:

```js
// `data` will look like this
// `data` はこのようになります
[
[
{ name: 'Alice', ... },
Expand All @@ -298,9 +298,9 @@ Also `data` is no longer just one API response. It's an array of multiple API re
]
```

### Example 2: Cursor or Offset Based Paginated API
### 例 2: カーソルまたはオフセットにもとづいたページネーション API

Let's say the API now requires a cursor and returns the next cursor alongside with the data:
API がカーソルを必要とし、データと一緒に次のカーソルを返すとしましょう:

```
GET /users?cursor=123&limit=10
Expand All @@ -315,27 +315,27 @@ GET /users?cursor=123&limit=10
}
```

We can change our `getKey` function to:
`getKey` 関数を次のように変更できます:

```jsx
const getKey = (pageIndex, previousPageData) => {
// reached the end
// 最後に到達した
if (previousPageData && !previousPageData.data) return null

// first page, we don't have `previousPageData`
// 最初のページでは、`previousPageData` がありません
if (pageIndex === 0) return `/users?limit=10`

// add the cursor to the API endpoint
// API のエンドポイントにカーソルを追加します
return `/users?cursor=${previousPageData.nextCursor}&limit=10`
}
```

### Advanced Features
### 高度な機能

[Here is an example](/examples/infinite-loading) showing how you can implement the following features with `useSWRInfinite`:
`useSWRInfinite` を使って次の機能を実装する方法は、[こちらに例があります](/examples/infinite-loading)

- loading states
- show a special UI if it's empty
- disable the "Load More" button if reached the end
- changeable data source
- refresh the entire list
- 状態の読み込み
- 空のときには特別な UI を表示する
- 最後に到達したときには「さらに読み込む」ボタンを無効化する
- 変更可能なデータソース
- リスト全体を更新する