リモートサーバー、API、またはデータベースから大量のデータレコードをフェッチする必要がある、Webアプリケーションの構築することがよくあります。たとえば、支払いシステムを構築している場合、何千ものトランザクションをフェッチしている可能性があります。それがソーシャルメディアアプリの場合なら、多くのユーザーコメント、プロファイル、またはアクティビティをフェッチしている可能性があります。いずれの場合でも、アプリを操作するエンドユーザーにとって便利な方法でデータを表示するためのソリューションがいくつかあります。
大きなデータセットを処理する方法の1つは、ページネーションを使用することですデータセットのサイズ(データセット内のレコードの総数)が事前にわかっている場合、ページネーションは、効果的に機能します。次に、エンドユーザーとページネーションコントロールとのやり取りに基づいて、データセット全体から必要なデータのチャンクのみを読み込みます。これは、Google 検索での検索結果を表示するために使用される手法です。
このチュートリアルでは、大きなデータセットをページネーションするために React を使用してカスタムのページネーションコンポーネントを構築する方法を学習します。世界の国々のページングされたビュー(既知のサイズのデータセット)を構築します。
これは、このチュートリアルで構築するもののデモです。
このチュートリアルを実行するには、次のものが必要です。
create-react-app
] create-react-appコマンドラインパッケージで、React アプリの boilerplate コードを作成します。npm 5.2
以降のバージョンを使用している場合、create-react-app を
グローバルに依存するものとしてインストールする必要があります。このチュートリアルは、Node v14.2.0、npm
v6.14.4、react
v16.13.1、react-scripts
v3.4.1で検証されました。
create-react-app
コマンドを使用して新しい React アプリケーションを起動します。アプリケーションには好きな名前を付けることができますが、ここでは、react-pagination
という名前を付けます。
- npx create-react-app react-pagination
次に、アプリケーションに必要な依存関係をインストールします。まず、端末のウィンドウを使ってプロジェクトディレクトリに移動します。
- cd react-pagination
次のコマンドを実行して、必要な依存関係をインストールします。
- npm install bootstrap@4.1.0 prop-types@15.6.1 react-flags@0.1.13 countries-api@2.0.1 node-sass@4.14.1
これにより、bootstrap``、prop-types
、react-flags
、countries-api
、node-sass
がインストールされます。
ここでは、デフォルトのスタイリングが必要となるので、アプリケーションの依存関係としてbootstrap
パッケージをインストールしました。Bootstrap の pagination
コンポーネントからのスタイルも使用します。
アプリケーションに Bootstrap を含めるには、src/index.js
ファイルを編集します。
- nano src/index.js
そして、他の import
ステートメントの前に次の行を追加します。
import "bootstrap/dist/css/bootstrap.min.css";
これで、Bootstrap スタイリングがアプリケーション全体で利用できるようになります。
アプリケーションの依存関係としてreact-flags
もインストールしました。アプリケーションから国旗アイコンにアクセスするには、アイコンの画像をアプリケーションの public
ディレクトリにコピーする必要があります。
public
ディレクトリに img
ディレクトリを作成します。
- mkdir public/img
flags
内の画像ファイルを img
にコピーします。
- cp -R node_modules/react-flags/vendor/flags public/img
これにより、すべての react-flag
イメージのコピーがアプリケーションに提供されます。
いくつかの依存関係を含めたので、react-pagination
プロジェクトディレクトリから npm
を指定して、次のコマンドを実行してアプリケーションを起動します。
- npm start
さて、アプリケーションを起動したので、開発を始めます。開発中にアプリケーションとの同期を維持するための_ライブリロード_機能を備えたブラウザタブが開いているかを確認してください。
この時点で、アプリケーションビューは次のスクリーンショットのように表示されるでしょう。
これで、コンポーネントの作成を開始する準備ができました。
CountryCard
コンポーネントの作成このステップでは、CountryCard
コンポーネントを作成します。CountryCard
コンポーネントは、指定された国の名前、地域、国旗をレンダリングします。
まず、src
ディレクトリに components
ディレクトリを作成してみましょう。
- mkdir src/components
そして、src/components
ディレクトリに新しい CountryCard.js
ファイルを作成します。
- nano src/components/CountryCard.js
そして、それに次のコードスニペットを追加します。
import React from 'react';
import PropTypes from 'prop-types';
import Flag from 'react-flags';
const CountryCard = props => {
const {
cca2: code2 = '', region = null, name = {}
} = props.country || {};
return (
<div className="col-sm-6 col-md-4 country-card">
<div className="country-card-container border-gray rounded border mx-2 my-3 d-flex flex-row align-items-center p-0 bg-light">
<div className="h-100 position-relative border-gray border-right px-2 bg-white rounded-left">
<Flag country={code2} format="png" pngSize={64} basePath="./img/flags" className="d-block h-100" />
</div>
<div className="px-3">
<span className="country-name text-dark d-block font-weight-bold">{ name.common }</span>
<span className="country-region text-secondary text-uppercase">{ region }</span>
</div>
</div>
</div>
)
}
CountryCard.propTypes = {
country: PropTypes.shape({
cca2: PropTypes.string.isRequired,
region: PropTypes.string.isRequired,
name: PropTypes.shape({
common: PropTypes.string.isRequired
}).isRequired
}).isRequired
};
export default CountryCard;
CountryCard
コンポーネントには、レンダリングする国に関するデータを含む国
の属性値が必要です。CountryCard
コンポーネントの propTypes
に見られるように、country
の属性オブジェクトには次のデータが含まれている必要があります。
cca2
- 2桁の国コード地域
- 国/地域(例:「アフリカ」)name.common
- 国の一般名(例:「ナイジェリア」)国オブジェクトのサンプルは、次のとおりです。
{
cca2: "NG",
region: "Africa",
name: {
common: "Nigeria"
}
}
また、react-flags
パッケージを使用して国旗をレンダリングする方法にも注目してください。必要な属性値とパッケージの使用方法のreact-flags詳細については、[react-flags
のドキュメント]をご覧ください。
これで、個々の CountryCard
コンポーネントが完成しました。最終的には、CountryCards
を複数回使用して、アプリケーションにさまざまな国旗と国情報を表示します。
Pagination
コンポーネントの作成このステップでは、Pagination
コンポーネントを作成します。Pagination
コンポーネントには、ページネーションコントロール上でのページの構築ロジック、レンダリング、切り替えが含まれます。
src/component
ディレクトリに Pagination.js
ファイルを新規作成します。
- nano src/components/Pagination.js
そして、それに次のコードスニペットを追加します。
import React, { Component, Fragment } from 'react';
import PropTypes from 'prop-types';
class Pagination extends Component {
constructor(props) {
super(props);
const { totalRecords = null, pageLimit = 30, pageNeighbours = 0 } = props;
this.pageLimit = typeof pageLimit === 'number' ? pageLimit : 30;
this.totalRecords = typeof totalRecords === 'number' ? totalRecords : 0;
// pageNeighbours can be: 0, 1 or 2
this.pageNeighbours = typeof pageNeighbours === 'number'
? Math.max(0, Math.min(pageNeighbours, 2))
: 0;
this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
this.state = { currentPage: 1 };
}
}
Pagination.propTypes = {
totalRecords: PropTypes.number.isRequired,
pageLimit: PropTypes.number,
pageNeighbours: PropTypes.number,
onPageChanged: PropTypes.func
};
export default Pagination;
Pagination
コンポーネントは、propTypes
オブジェクトで指定された 4 つの特別な属性値を取ることができます。
onPageChanged
は、現在のページが変更された場合にのみ、現在のページネーション状態のデータで呼び出される関数です。totalRecords
は、ページ分割するレコードの総数を示します。これは、必須項目です。pageLimit
は、1 ページあたりに表示するレコード数を示します。指定しない場合は、constructor()
で定義されているようにデフォルトで 30
となります。pageNeighbours
は、現在のページの両側に表示する追加のページ番号の数を示します。最小値は 0
、最大値は 2
です。指定しない場合は、constructor()
で定義されているように 0 が
デフォルトとなります。次の画像は、pageNeighbours
の属性値を変えた場合の効果を示しています。
constructor()
関数では、以下のように総ページ数を計算します。
this.totalPages = Math.ceil(this.totalRecords / this.pageLimit);
ここでは Math.ceil()
を使用して、ページ数の合計を整数値で取得することに注意してください。これにより、特に1ページあたりの表示件数よりも余剰レコードの数が少ない場合には、余剰レコードを確実に最後のページに取り込むことができるます。
最後に、currentPage
プロパティを 1
に設定して状態を初期化しました。現在アクティブなページを内部的に追跡するためには、この状態プロパティが必要です。
次に、ページ番号を生成する方法を作成します。
import
宣言に続くPagination
クラスの前に、次の定数と 範囲
関数を追加します。
// ...
const LEFT_PAGE = 'LEFT';
const RIGHT_PAGE = 'RIGHT';
/**
* Helper method for creating a range of numbers
* range(1, 5) => [1, 2, 3, 4, 5]
*/
const range = (from, to, step = 1) => {
let i = from;
const range = [];
while (i <= to) {
range.push(i);
i += step;
}
return range;
}
Pagination
クラスでは、constructor
の後ろに、Numbersメソッドを追加します。 ``
class Pagination extends Component {
// ...
/**
* Let's say we have 10 pages and we set pageNeighbours to 2
* Given that the current page is 6
* The pagination control will look like the following:
*
* (1) < {4 5} [6] {7 8} > (10)
*
* (x) => terminal pages: first and last page(always visible)
* [x] => represents current page
* {...x} => represents page neighbours
*/
fetchPageNumbers = () => {
const totalPages = this.totalPages;
const currentPage = this.state.currentPage;
const pageNeighbours = this.pageNeighbours;
/**
* totalNumbers: the total page numbers to show on the control
* totalBlocks: totalNumbers + 2 to cover for the left(<) and right(>) controls
*/
const totalNumbers = (this.pageNeighbours * 2) + 3;
const totalBlocks = totalNumbers + 2;
if (totalPages > totalBlocks) {
const startPage = Math.max(2, currentPage - pageNeighbours);
const endPage = Math.min(totalPages - 1, currentPage + pageNeighbours);
let pages = range(startPage, endPage);
/**
* hasLeftSpill: has hidden pages to the left
* hasRightSpill: has hidden pages to the right
* spillOffset: number of hidden pages either to the left or to the right
*/
const hasLeftSpill = startPage > 2;
const hasRightSpill = (totalPages - endPage) > 1;
const spillOffset = totalNumbers - (pages.length + 1);
switch (true) {
// handle: (1) < {5 6} [7] {8 9} (10)
case (hasLeftSpill && !hasRightSpill): {
const extraPages = range(startPage - spillOffset, startPage - 1);
pages = [LEFT_PAGE, ...extraPages, ...pages];
break;
}
// handle: (1) {2 3} [4] {5 6} > (10)
case (!hasLeftSpill && hasRightSpill): {
const extraPages = range(endPage + 1, endPage + spillOffset);
pages = [...pages, ...extraPages, RIGHT_PAGE];
break;
}
// handle: (1) < {4 5} [6] {7 8} > (10)
case (hasLeftSpill && hasRightSpill):
default: {
pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];
break;
}
}
return [1, ...pages, totalPages];
}
return range(1, totalPages);
}
}
ここでは、最初に2つの定数:LEFT_PAGE
と RIGHT_PAGE
を定義します。これらの定数は、それぞれ左と右に移動するためのページコントロールがあるポイントを示すために使用されます。
また、数値の範囲を生成するのに役立つヘルパー range()
関数も定義しました。
**注:**プロジェクトで Lodash のようなユーティリティライブラリを使用する場合は、代わりに Lodash が提供する _.range()
関数を使用します。次のコードスニペットは、定義した range()
関数と Lodash の関数の違いを示しています。
range(1, 5); // returns [1, 2, 3, 4, 5]
_.range(1, 5); // returns [1, 2, 3, 4]
次に、Pagination
クラスで fetchPageNumbers()
メソッドを定義しました。このメソッドは、ページネーションコントロールに表示されるページ番号を生成するためのコアロジックを処理します。最初のページと最後のページが常に表示されているようにします。
まず、いくつかの変数を定義しました。 totalNumbers
は、コントロールに表示される合計ページ番号を表します。totalBlocks
は、表示される合計ページ数に加えて、左右のページインジケーター用の 2 つの追加ブロックを表します。
totalPages
が totalBlocks
以下の場合、1
からtotalPages
までの範囲の数値を返します。それ以外の場合は、ページ番号の配列を返します。ページがそれぞれ左と右に流れるポイントで、LEFT_PAGE
と RIGHT_PAGE
を返します。
ただし、ページネーションコントロールによって、最初のページと最後のページが常に表示されているのがわかります。左右のページコントロールが内側に表示されます。
次に、render()
メソッドを追加して、ページネーションコントロールをレンダリングできるようにします。
Pagination
クラスで、constructor
と fetchPageNumbers
メソッドの後に、次の render
メソッドを追加します。
class Pagination extends Component {
// ...
render() {
if (!this.totalRecords || this.totalPages === 1) return null;
const { currentPage } = this.state;
const pages = this.fetchPageNumbers();
return (
<Fragment>
<nav aria-label="Countries Pagination">
<ul className="pagination">
{ pages.map((page, index) => {
if (page === LEFT_PAGE) return (
<li key={index} className="page-item">
<a className="page-link" href="#" aria-label="Previous" onClick={this.handleMoveLeft}>
<span aria-hidden="true">«</span>
<span className="sr-only">Previous</span>
</a>
</li>
);
if (page === RIGHT_PAGE) return (
<li key={index} className="page-item">
<a className="page-link" href="#" aria-label="Next" onClick={this.handleMoveRight}>
<span aria-hidden="true">»</span>
<span className="sr-only">Next</span>
</a>
</li>
);
return (
<li key={index} className={`page-item${ currentPage === page ? ' active' : ''}`}>
<a className="page-link" href="#" onClick={ this.handleClick(page) }>{ page }</a>
</li>
);
}) }
</ul>
</nav>
</Fragment>
);
}
}
ここでは、前に作成した fetchPageNumbers()
メソッドを呼び出して、ページ番号の配列
を生成します。次に、Array.prototype.map()
を使用して各ページ番号をレンダリングします。クリックを処理するために、レンダリングされた各ページ番号にクリックイベントハンドラーを登録しているか注意してください。
また、totalRecords
属性値(props)が Pagination
コンポーネントに正しくアサートされていない場合や、1
ページしかない場合には、ページ分割コントロールはレンダリングされないことに注意してください。
最後に、イベントハンドラーのメソッドを定義します。
Pagination
クラスでは、constructor
と fetchPageNumbers
メソッド、そしてrender
メソッドの後に、次のように追加します。
class Pagination extends Component {
// ...
componentDidMount() {
this.gotoPage(1);
}
gotoPage = page => {
const { onPageChanged = f => f } = this.props;
const currentPage = Math.max(0, Math.min(page, this.totalPages));
const paginationData = {
currentPage,
totalPages: this.totalPages,
pageLimit: this.pageLimit,
totalRecords: this.totalRecords
};
this.setState({ currentPage }, () => onPageChanged(paginationData));
}
handleClick = page => evt => {
evt.preventDefault();
this.gotoPage(page);
}
handleMoveLeft = evt => {
evt.preventDefault();
this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);
}
handleMoveRight = evt => {
evt.preventDefault();
this.gotoPage(this.state.currentPage + (this.pageNeighbours * 2) + 1);
}
}
状態を変更する gotoPage()
メソッドを定義し、指定されたページに currentPage
を設定します。これにより、ページ
引数の最小値は 1
、最大値としてページ総数を確保します。最後に属性値(prop)として渡された onPageChanged()
関数を、新しいページネーション状態を示すデータとともに呼び出します。
コンポーネントがマウントされると、componentDidMount()
のライフサイクルメソッドにあるように、this.gotoPage(1)
を呼び出して最初のページに移動します。
handleMoveLeft()
と handleMoveRight()
で (this.pageNeighbours * 2)
を使用して、現在のページ番号に基づいてページ数をそれぞれ左へ、そして右へスライドさせていることに注意してください。
左から右への動きのインタラクションのデモです。
これで、ページネーション
コンポーネントが完了しました。ユーザーは、このコンポーネントのナビゲーションコントロールを操作して、国旗のさまざまなページを表示できます。
アプリ
コンポーネントの構築これで、CountryCard
と ページネーション
のコンポーネントができたので、アプリ
コンポーネントでそれらを確認できます。
src
ディレクトリの App.js
ファイルを変更します。
- nano src/App.js
App.js
の内容を、次のコード行に置き換えます。
import React, { Component } from 'react';
import Countries from 'countries-api';
import './App.css';
import Pagination from './components/Pagination';
import CountryCard from './components/CountryCard';
class App extends Component {
state = { allCountries: [], currentCountries: [], currentPage: null, totalPages: null }
componentDidMount() {
const { data: allCountries = [] } = Countries.findAll();
this.setState({ allCountries });
}
onPageChanged = data => {
const { allCountries } = this.state;
const { currentPage, totalPages, pageLimit } = data;
const offset = (currentPage - 1) * pageLimit;
const currentCountries = allCountries.slice(offset, offset + pageLimit);
this.setState({ currentPage, currentCountries, totalPages });
}
}
export default App;
ここでは、次の属性を使用して App
コンポーネントの状態を初期化します。
allCountries
- これはアプリ内のすべての国の配列です。 空の配列([]
)に初期化されます。currentCountries
- これは、現在アクティブなページに表示されるすべての国の配列です。空の配列([]
)に初期化されます。currentPage
- 現在アクティブなページのページ番号です。null
に初期化されました。totalPages
- すべての国レコードの総ページ数。null
に初期化されました。次に、componentDidMount()
ライフサイクルメソッドで、Countries.findAll()
を呼び出すことにより、countries-api
パッケージを使用してすべての世界の国をフェッチします。次に、アプリの状態を更新し、allCountries
にすべての世界の国を含めるように設定します。パッケージのcountries-api詳細については[、countries-api
のドキュメント]を参照してください。
最後に、onPageChanged()
メソッドを定義しました。このメソッドは、ページネーションコントロールから新しいページに移動するたびに呼び出されます。このメソッドは、Pagination
コンポーネントの onPageChanged
プロパティに渡されます。
この方法で注意する価値のある 2 つの行があります。1つ目は、この行です。
const offset = (currentPage - 1) * pageLimit;
オフセット
値は、現在のページのレコードをフェッチするための開始インデックスを示します。(currentPage-1)
を使用すると、オフセットがゼロベースになります。たとえば、ページごとに 25
レコードを表示していて、現在ページ 5
を表示しているとします。その場合、オフセット
は((5 - 1) * 25 = 100)
になります。
データベースからオンデマンドでレコードをフェッチしている場合の例として、ここにオフセットの使用方法を示すサンプル SQL クエリを示します。
SELECT * FROM `countries` LIMIT 100, 25
データベースや外部ソースからレコードをフェッチしていないため、現在のページに表示する必要のあるレコードのチャンクを抽出する方法が必要です。
2 つ目は、この行です。
const currentCountries = allCountries.slice(offset, offset + pageLimit);
ここでは、Array.prototype.slice()
メソッドを使用して、オフセット
をスライスの開始インデックスとして渡し、(offset + pageLimit)
をスライスを終了する前のインデックスとして渡すことにより、allCountries
から必要なレコードのチャンクを抽出します。
注意: このチュートリアルでは、外部ソースからレコードをフェッチしませんでした。実際のアプリケーションでは、おそらくデータベースまたは API からレコードをフェッチします。レコードをフェッチするためのロジックについては、App
コンポーネントのメソッド
に進みます。
架空の API エンドポイント/ api / countrys?page = {current_page}&limit = {page_limit}
があるとします。次のスニペットは、axios
HTTP パッケージを使用して API からオンデマンドで国をフェッチする方法を示しています。
onPageChanged = data => {
const { currentPage, totalPages, pageLimit } = data;
axios.get(`/api/countries?page=${currentPage}&limit=${pageLimit}`)
.then(response => {
const currentCountries = response.data.countries;
this.setState({ currentPage, currentCountries, totalPages });
});
}
これで、render()
メソッドを追加して App
コンポーネントを完成させることができます。
App
クラスで、componentDidMount
と onPageChanged
の後に、次の render
メソッドを追加します。
class App extends Component {
// ... other methods here ...
render() {
const { allCountries, currentCountries, currentPage, totalPages } = this.state;
const totalCountries = allCountries.length;
if (totalCountries === 0) return null;
const headerClass = ['text-dark py-2 pr-4 m-0', currentPage ? 'border-gray border-right' : ''].join(' ').trim();
return (
<div className="container mb-5">
<div className="row d-flex flex-row py-5">
<div className="w-100 px-4 py-5 d-flex flex-row flex-wrap align-items-center justify-content-between">
<div className="d-flex flex-row align-items-center">
<h2 className={headerClass}>
<strong className="text-secondary">{totalCountries}</strong> Countries
</h2>
{ currentPage && (
<span className="current-page d-inline-block h-100 pl-4 text-secondary">
Page <span className="font-weight-bold">{ currentPage }</span> / <span className="font-weight-bold">{ totalPages }</span>
</span>
) }
</div>
<div className="d-flex flex-row py-4 align-items-center">
<Pagination totalRecords={totalCountries} pageLimit={18} pageNeighbours={1} onPageChanged={this.onPageChanged} />
</div>
</div>
{ currentCountries.map(country => <CountryCard key={country.cca3} country={country} />) }
</div>
</div>
);
}
}
render()
メソッドでは、国の総数、現在のページ、ページの総数、<Pagination>
コントロール、そして現在のページの国ごとの<CountryCard>
をレンダリングします。
前に定義した onPageChanged()
メソッドを <Pagination>
コントロールの onPage
Changed プロパティに渡したことに注意してください。これは、Pagination
コンポーネントからページの変更をキャプチャするために非常に重要です。また、ページごとに 18
ヵ国を表示しています。
この時点で、アプリは次のスクリーンショットのようになります。
これで、複数の CountryCard
コンポーネントと、コンテンツを別々のページに分割する Pagination
コンポーネントを表示 する App
コンポーネント が 表示 さ れ ます 。続いて、アプリケーションのスタイリングについて説明します。
以前に作成したコンポーネントにいくつかのカスタムクラスを追加したのにお気づきかもしれません。src /App.scss
ファイルでこれらのクラスのいくつかのスタイルルールを定義しましょう。
- nano src/App.scss
App.scss
ファイルは、次のスニペットのようになります。
/* Declare some variables */
$base-color: #ced4da;
$light-background: lighten(desaturate($base-color, 50%), 12.5%);
.current-page {
font-size: 1.5rem;
vertical-align: middle;
}
.country-card-container {
height: 60px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.country-name {
font-size: 0.9rem;
}
.country-region {
font-size: 0.7rem;
}
.current-page,
.country-name,
.country-region {
line-height: 1;
}
// Override some Bootstrap pagination styles
ul.pagination {
margin-top: 0;
margin-bottom: 0;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
li.page-item.active {
a.page-link {
color: saturate(darken($base-color, 50%), 5%) !important;
background-color: saturate(lighten($base-color, 7.5%), 2.5%) !important;
border-color: $base-color !important;
}
}
a.page-link {
padding: 0.75rem 1rem;
min-width: 3.5rem;
text-align: center;
box-shadow: none !important;
border-color: $base-color !important;
color: saturate(darken($base-color, 30%), 10%);
font-weight: 900;
font-size: 1rem;
&:hover {
background-color: $light-background;
}
}
}
App.css
ではなく App.scss
を参照するように App.js
ファイルを変更します。
**注:**この詳細は、[Create React App のドキュメント] を参照してください。create-react-app-sass
- nano src/App.js
import React, { Component } from 'react';
import Countries from 'countries-api';
import './App.scss';
import Pagination from './components/Pagination';
import CountryCard from './components/CountryCard';
スタイルを追加すると、アプリは、次のスクリーンショットのようになります。
これで、追加のカスタムスタイリングを備えた完全なアプリケーションができました。カスタムスタイルを使用して、Bootstrap などのライブラリによって提供されるデフォルトのスタイルを変更および拡張できます。
このチュートリアルでは、React アプリケーションを使用して、カスタムページネーションウィジェットを作成しました。API を呼び出したり、データベースバックエンドとやり取りしたりしていませんが、アプリケーションがそのようなやり取りを要求する場合があります。ここで使用されているアプローチに制限される必要はありません。アプリケーションの要件に合わせて拡張できます。
完全なソースコードについては、GitHub の [build-react-agination-demo] pagination-demoリポジトリをご覧ください。[また、チュートリアルのライブデモは、CodeSandbox で]入手することもできますcode-demo。
React についての知識を深めたい場合は、How To Code in React.js(React.js のコーディング方法) シリーズを参照するか、演習とプログラミングプロジェクトの React トピックページをご覧ください。
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!