はじめに

リモートサーバー、API、またはデータベースから大量のデータレコードをフェッチする必要がある、Webアプリケーションの構築することがよくあります。たとえば、支払いシステムを構築している場合、何千ものトランザクションをフェッチしている可能性があります。それがソーシャルメディアアプリの場合なら、多くのユーザーコメント、プロファイル、またはアクティビティをフェッチしている可能性があります。いずれの場合でも、アプリを操作するエンドユーザーにとって便利な方法でデータを表示するためのソリューションがいくつかあります。

大きなデータセットを処理する方法の1つは[_、ページネーション_][pagination]を使用することですデータセットのサイズ(データセット内のレコードの総数)が事前にわかっている場合、ページネーションは、効果的に機能します。次に、エンドユーザーとページネーションコントロールとのやり取りに基づいて、データセット全体から必要なデータのチャンクのみを読み込みます。これは、Google 検索での検索結果を表示するために使用される手法です。

このチュートリアルでは、大きなデータセットをページネーションするために React を使用してカスタムのページネーションコンポーネントを構築する方法を学習します。世界の国々のページングされたビュー(既知のサイズのデータセット)を構築します。

これは、このチュートリアルで構築するもののデモです。

前提条件

react illustration for: 前提条件

このチュートリアルを実行するには、次のものが必要です。

  • [create-react-app] [create-react-app]コマンドラインパッケージで、React アプリの boilerplate コードを作成します。npm 5.2 以降のバージョンを使用している場合、create-react-app をグローバルに依存するものとしてインストールする必要があります。
  • そして、このチュートリアルでは、すでに React に精通している方々を前提として説明をしてゆきます。そうでない場合は、How To Code in React.js(React.js のコーディング方法)シリーズをチェックして、React の詳細を確認してください。

このチュートリアルは、Node v14.2.0、npm v6.14.4、react v16.13.1、react-scriptsv3.4.1で検証されました。

ステップ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-typesreact-flagscountries-apinode-sassがインストールされます。

ここでは、デフォルトのスタイリングが必要となるので、アプリケーションの依存関係としてbootstrap パッケージをインストールしました。Bootstrap の pagination コンポーネントからのスタイルも使用します。

アプリケーションに Bootstrap を含めるには、src/index.js ファイルを編集します。

				
					
nano src/index.js

				
			

そして、他の import ステートメントの前に次の行を追加します。

				
					
[label src/index.js]

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

				
			

さて、アプリケーションを起動したので、開発を始めます。開発中にアプリケーションとの同期を維持するための_ライブリロード_機能を備えたブラウザタブが開いているかを確認してください。

この時点で、アプリケーションビューは次のスクリーンショットのように表示されるでしょう。

これで、コンポーネントの作成を開始する準備ができました。

ステップ2 — CountryCard コンポーネントの作成

このステップでは、CountryCard コンポーネントを作成します。CountryCard コンポーネントは、指定された国の名前、地域、国旗をレンダリングします。

まず、src ディレクトリに components ディレクトリを作成してみましょう。

				
					
mkdir src/components

				
			

そして、src/components ディレクトリに新しい CountryCard.js ファイルを作成します。

				
					
nano src/components/CountryCard.js

				
			

そして、それに次のコードスニペットを追加します。

				
					
[label 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 を複数回使用して、アプリケーションにさまざまな国旗と国情報を表示します。

<!– NOTE: add section conclusion –>

ステップ3 — Pagination コンポーネントの作成

このステップでは、Pagination コンポーネントを作成します。Pagination コンポーネントには、ページネーションコントロール上でのページの構築ロジック、レンダリング、切り替えが含まれます。

src/component ディレクトリに Pagination.js ファイルを新規作成します。

				
					
nano src/components/Pagination.js

				
			

そして、それに次のコードスニペットを追加します。

				
					
[label 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 クラスの前に、次の定数と 範囲 関数を追加します。

				
					
[label src/components/Pagination.js]

// ...



const LEFT_PAGE = 'LEFT';

const RIGHT_PAGE = 'RIGHT';



/**

 * Helper method for creating a range of numbers

 * range(1, 5) =&gt; [1, 2, 3, 4, 5]

 */

const range = (from, to, step = 1) =&gt; {

 let i = from;

 const range = [];



 while (i &lt;= to) {

 range.push(i);

 i += step;

 }



 return range;

}

				
			

Pagination クラスでは、constructor の後ろに、Numbersメソッドを追加します。 “

				
					
[label src/components/Pagination.js]

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) &lt; {4 5} [6] {7 8} &gt; (10)

 *

 * (x) =&gt; terminal pages: first and last page(always visible)

 * [x] =&gt; represents current page

 * {...x} =&gt; represents page neighbours

 */

 fetchPageNumbers = () =&gt; {

 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(&lt;) and right(&gt;) controls

 */

 const totalNumbers = (this.pageNeighbours * 2) + 3;

 const totalBlocks = totalNumbers + 2;



 if (totalPages &gt; 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 &gt; 2;

 const hasRightSpill = (totalPages - endPage) &gt; 1;

 const spillOffset = totalNumbers - (pages.length + 1);



 switch (true) {

 // handle: (1) &lt; {5 6} [7] {8 9} (10)

 case (hasLeftSpill &amp;&amp; !hasRightSpill): {

 const extraPages = range(startPage - spillOffset, startPage - 1);

 pages = [LEFT_PAGE, ...extraPages, ...pages];

 break;

 }



 // handle: (1) {2 3} [4] {5 6} &gt; (10)

 case (!hasLeftSpill &amp;&amp; hasRightSpill): {

 const extraPages = range(endPage + 1, endPage + spillOffset);

 pages = [...pages, ...extraPages, RIGHT_PAGE];

 break;

 }



 // handle: (1) &lt; {4 5} [6] {7 8} &gt; (10)

 case (hasLeftSpill &amp;&amp; hasRightSpill):

 default: {

 pages = [LEFT_PAGE, ...pages, RIGHT_PAGE];

 break;

 }

 }



 return [1, ...pages, totalPages];

 }



 return range(1, totalPages);

 }

}

				
			

ここでは、最初に2つの定数:LEFT_PAGERIGHT_PAGE を定義します。これらの定数は、それぞれ左と右に移動するためのページコントロールがあるポイントを示すために使用されます。

また、数値の範囲を生成するのに役立つヘルパー range() 関数も定義しました。

注:プロジェクトで [Lodash ][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 つの追加ブロックを表します。

totalPagestotalBlocks 以下の場合、1 からtotalPages までの範囲の数値を返します。それ以外の場合は、ページ番号の配列を返します。ページがそれぞれ左と右に流れるポイントで、LEFT_PAGERIGHT_PAGE を返します。

ただし、ページネーションコントロールによって、最初のページと最後のページが常に表示されているのがわかります。左右のページコントロールが内側に表示されます。

次に、render() メソッドを追加して、ページネーションコントロールをレンダリングできるようにします。

Pagination クラスで、constructorfetchPageNumbers メソッドの後に、次の render メソッドを追加します。

				
					
[label src/components/Pagination.js]

class Pagination extends Component {

 // ...



 render() {

 if (!this.totalRecords || this.totalPages === 1) return null;



 const { currentPage } = this.state;

 const pages = this.fetchPageNumbers();



 return (

 &lt;Fragment&gt;

 &lt;nav aria-label="Countries Pagination"&gt;

 &lt;ul className="pagination"&gt;

 { pages.map((page, index) =&gt; {



 if (page === LEFT_PAGE) return (

 &lt;li key={index} className="page-item"&gt;

 &lt;a className="page-link" href="#" aria-label="Previous" onClick={this.handleMoveLeft}&gt;

 &lt;span aria-hidden="true"&gt;&amp;laquo;&lt;/span&gt;

 &lt;span className="sr-only"&gt;Previous&lt;/span&gt;

 &lt;/a&gt;

 &lt;/li&gt;

 );



 if (page === RIGHT_PAGE) return (

 &lt;li key={index} className="page-item"&gt;

 &lt;a className="page-link" href="#" aria-label="Next" onClick={this.handleMoveRight}&gt;

 &lt;span aria-hidden="true"&gt;&amp;raquo;&lt;/span&gt;

 &lt;span className="sr-only"&gt;Next&lt;/span&gt;

 &lt;/a&gt;

 &lt;/li&gt;

 );



 return (

 &lt;li key={index} className={`page-item${ currentPage === page ? ' active' : ''}`}&gt;

 &lt;a className="page-link" href="#" onClick={ this.handleClick(page) }&gt;{ page }&lt;/a&gt;

 &lt;/li&gt;

 );



 }) }



 &lt;/ul&gt;

 &lt;/nav&gt;

 &lt;/Fragment&gt;

 );

 }

}

				
			

ここでは、前に作成した fetchPageNumbers() メソッドを呼び出して、ページ番号の配列を生成します。次に、Array.prototype.map() を使用して各ページ番号をレンダリングします。クリックを処理するために、レンダリングされた各ページ番号にクリックイベントハンドラーを登録しているか注意してください。

また、totalRecords 属性値(props)が Pagination コンポーネントに正しくアサートされていない場合や、1 ページしかない場合には、ページ分割コントロールはレンダリングされないことに注意してください。

最後に、イベントハンドラーのメソッドを定義します。

Pagination クラスでは、constructorfetchPageNumbers メソッド、そしてrender メソッドの後に、次のように追加します。

				
					
[label src/components/Pagination.js]

class Pagination extends Component {

 // ...



 componentDidMount() {

 this.gotoPage(1);

 }



 gotoPage = page =&gt; {

 const { onPageChanged = f =&gt; 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 }, () =&gt; onPageChanged(paginationData));

 }



 handleClick = page =&gt; evt =&gt; {

 evt.preventDefault();

 this.gotoPage(page);

 }



 handleMoveLeft = evt =&gt; {

 evt.preventDefault();

 this.gotoPage(this.state.currentPage - (this.pageNeighbours * 2) - 1);

 }



 handleMoveRight = evt =&gt; {

 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) を使用して、現在のページ番号に基づいてページ数をそれぞれ左へ、そして右へスライドさせていることに注意してください。

左から右への動きのインタラクションのデモです。

これで、ページネーションコンポーネントが完了しました。ユーザーは、このコンポーネントのナビゲーションコントロールを操作して、国旗のさまざまなページを表示できます。

ステップ4 — アプリコンポーネントの構築

これで、CountryCardページネーションのコンポーネントができたので、アプリコンポーネントでそれらを確認できます。

src ディレクトリの App.js ファイルを変更します。

				
					
nano src/App.js

				
			

App.js の内容を、次のコード行に置き換えます。

				
					
[label src/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 =&gt; {

 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][axios]HTTP パッケージを使用して API からオンデマンドで国をフェッチする方法を示しています。

				
					
onPageChanged = data =&gt; {

 const { currentPage, totalPages, pageLimit } = data;



 axios.get(`/api/countries?page=${currentPage}&amp;limit=${pageLimit}`)

 .then(response =&gt; {

 const currentCountries = response.data.countries;

 this.setState({ currentPage, currentCountries, totalPages });

 });

}

				
			

これで、render() メソッドを追加して App コンポーネントを完成させることができます。

App クラスで、componentDidMountonPageChanged の後に、次の render メソッドを追加します。

				
					
[label src/App.js]

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 (

 &lt;div className="container mb-5"&gt;

 &lt;div className="row d-flex flex-row py-5"&gt;

 &lt;div className="w-100 px-4 py-5 d-flex flex-row flex-wrap align-items-center justify-content-between"&gt;

 &lt;div className="d-flex flex-row align-items-center"&gt;

 &lt;h2 className={headerClass}&gt;

 &lt;strong className="text-secondary"&gt;{totalCountries}&lt;/strong&gt; Countries

 &lt;/h2&gt;

 { currentPage &amp;&amp; (

 &lt;span className="current-page d-inline-block h-100 pl-4 text-secondary"&gt;

 Page &lt;span className="font-weight-bold"&gt;{ currentPage }&lt;/span&gt; / &lt;span className="font-weight-bold"&gt;{ totalPages }&lt;/span&gt;

 &lt;/span&gt;

 ) }

 &lt;/div&gt;

 &lt;div className="d-flex flex-row py-4 align-items-center"&gt;

 &lt;Pagination totalRecords={totalCountries} pageLimit={18} pageNeighbours={1} onPageChanged={this.onPageChanged} /&gt;

 &lt;/div&gt;

 &lt;/div&gt;

 { currentCountries.map(country =&gt; &lt;CountryCard key={country.cca3} country={country} /&gt;) }

 &lt;/div&gt;

 &lt;/div&gt;

 );

 }

}

				
			

render() メソッドでは、国の総数、現在のページ、ページの総数、<Pagination> コントロール、そして現在のページの国ごとの<CountryCard> をレンダリングします。

前に定義した onPageChanged() メソッドを <Pagination> コントロールの onPageChanged プロパティに渡したことに注意してください。これは、Pagination コンポーネントからページの変更をキャプチャするために非常に重要です。また、ページごとに 18 ヵ国を表示しています。

この時点で、アプリは次のスクリーンショットのようになります。

これで、複数の CountryCard コンポーネントと、コンテンツを別々のページに分割する Pagination コンポーネントを表示 する App コンポーネント が 表示 さ れ ます 。続いて、アプリケーションのスタイリングについて説明します。

ステップ5 — カスタムスタイルの追加

以前に作成したコンポーネントにいくつかのカスタムクラスを追加したのにお気づきかもしれません。src /App.scss ファイルでこれらのクラスのいくつかのスタイルルールを定義しましょう。

				
					
nano src/App.scss

				
			

App.scss ファイルは、次のスニペットのようになります。

				
					
[label src/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

				
			
				
					
[label src/App.js]

import React, { Component } from 'react';

import Countries from 'countries-api';

import &lt;^&gt;'./App.scss'&lt;^&gt;;

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 トピックページをご覧ください。

[axios]: https://github.com/axios/axios

[code-demo]: https://codesandbox.io/s/l29rokm9rm

[countries-api]: https://www.npmjs.com/package/countries-api

[create-react-app]: https://github.com/facebook/create-react-app

[create-react-app-sass]: https://create-react-app.dev/docs/adding-a-sass-stylesheet/

[lodash]: https://lodash.com/

[nodejs]: https://nodejs.org/en/

[npm]: https://npmjs.com/

[pagination]: https://en.wikipedia.org/wiki/Pagination

[pagination-demo]: https://github.com/do-community/build-react-pagination-demo

[reactjs]: https://reactjs.org/docs/

[react-flags]: https://github.com/wiredmax/react-flags