小道具と反応してラッパーコンポーネントを作成する方法

提供:Dev Guides
移動先:案内検索

著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

このチュートリアルでは、 React JavaScriptライブラリを使用して、小道具を使用してラッパーコンポーネントを作成します。 ラッパーコンポーネントは、不明なコンポーネントを囲むコンポーネントであり、子コンポーネントを表示するためのデフォルトの構造を提供します。 このパターンは、モーダル、テンプレートページ、情報タイルなど、デザイン全体で繰り返し使用されるユーザーインターフェイス(UI)要素を作成する場合に役立ちます。

ラッパーコンポーネントを作成するには、最初にRESTおよびSpread演算子を使用して未使用の小道具を収集し、ネストされたコンポーネントに渡す方法を学習します。 次に、組み込みのchildrenコンポーネントを使用して、ネストされたコンポーネントをHTML要素であるかのようにJSXにラップするコンポーネントを作成します。 最後に、コンポーネントを小道具として渡して、コンポーネント内の複数の場所にカスタムJSXを埋め込むことができる柔軟なラッパーを作成します。

チュートリアルでは、動物データのリストをカードの形式で表示するコンポーネントを作成します。 柔軟なラッピングコンポーネントを作成しながら、データを分割してコンポーネントをリファクタリングする方法を学びます。 このチュートリアルを終了すると、高度なプロップ技術を使用して、アプリケーションの成長と変化に応じて拡張および適応する再利用可能なコンポーネントを作成する、実用的なアプリケーションが完成します。

:最初のステップでは、チュートリアル演習を作成するための空白のプロジェクトを設定します。 すでに作業中のプロジェクトがあり、小道具の操作に直接進みたい場合は、ステップ2から始めてください。


前提条件

ステップ1—空のプロジェクトを作成する

このステップでは、 Create ReactAppを使用して新しいプロジェクトを作成します。 次に、プロジェクトをブートストラップするときにインストールされるサンプルプロジェクトと関連ファイルを削除します。 最後に、コンポーネントを整理するための単純なファイル構造を作成します。 これにより、次のステップでこのチュートリアルのラッパーアプリケーションを構築するための確固たる基盤が得られます。

まず、新しいプロジェクトを作成します。 コマンドラインで次のスクリプトを実行し、create-react-appを使用して新しいプロジェクトをインストールします。

npx create-react-app wrapper-tutorial

プロジェクトが終了したら、次のディレクトリに移動します。

cd wrapper-tutorial

新しいターミナルタブまたはウィンドウで、 CreateReactApp開始スクリプトを使用してプロジェクトを開始します。 ブラウザは変更時に自動更新されるため、作業中はこのスクリプトを実行したままにします。

npm start

実行中のローカルサーバーを取得します。 プロジェクトがブラウザウィンドウで開かなかった場合は、 http:// localhost:3000/でプロジェクトを開くことができます。 これをリモートサーバーから実行している場合、アドレスはhttp://your_domain:3000になります。

ブラウザには、CreateReactAppの一部として含まれている単純なReactアプリケーションが読み込まれます。

完全に新しいカスタムコンポーネントのセットを構築するので、空のプロジェクトを作成できるように、ボイラープレートコードをクリアすることから始める必要があります。

まず、テキストエディタでsrc/App.jsを開きます。 これは、ページに挿入されるルートコンポーネントです。 すべてのコンポーネントはここから始まります。 App.jsの詳細については、 Create ReactAppを使用してReactプロジェクトをセットアップする方法を参照してください。

次のコマンドでsrc/App.jsを開きます。

nano src/App.js

次のようなファイルが表示されます。

ラッパー-tutorial/src / App.js

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

import logo from './logo.svg';の行を削除します。 次に、returnステートメントのすべてを置き換えて、空のタグのセット<></>を返します。 これにより、何も返さない有効なページが表示されます。 最終的なコードは次のようになります。

ラッパー-tutorial/src / App.js

import React from 'react';
import './App.css';

function App() {
  return <></>;
}

export default App;

テキストエディタを保存して終了します。

最後に、ロゴを削除します。 アプリケーションで使用することはないので、作業中に未使用のファイルを削除する必要があります。 長期的には混乱からあなたを救うでしょう。

ターミナルウィンドウで、次のコマンドを入力します。

rm src/logo.svg

ブラウザを見ると、空白の画面が表示されます。

サンプルのCreateReactAppプロジェクトをクリアしたので、単純なファイル構造を作成します。 これにより、コンポーネントを分離して独立させることができます。

srcディレクトリにcomponentsというディレクトリを作成します。 これにより、すべてのカスタムコンポーネントが保持されます。

mkdir src/components

各コンポーネントには、スタイル、存在する場合は画像、およびテストとともにコンポーネントファイルを保存するための独自のディレクトリがあります。

Appのディレクトリを作成します。

mkdir src/components/App

すべてのAppファイルをそのディレクトリに移動します。 ワイルドカード*を使用して、ファイル拡張子に関係なく、App.で始まるファイルを選択します。 次に、mvコマンドを使用して、それらを新しいディレクトリに配置します。

mv src/App.* src/components/App

次に、index.jsの相対インポートパスを更新します。これは、プロセス全体をブートストラップするルートコンポーネントです。

nano src/index.js

importステートメントは、AppディレクトリのApp.jsファイルを指す必要があるため、次の強調表示された変更を行います。

ラッパー-tutorial/src / index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App/App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

ファイルを保存して終了します。

プロジェクトが設定されたので、最初のコンポーネントを作成できます。

ステップ2—...propsを使用して未使用の小道具を収集する

このステップでは、動物のグループに関するデータのセットを表示するコンポーネントを作成します。 コンポーネントには、情報を視覚的に表示するための2番目のネストされたコンポーネントが含まれます。 親とネストされたコンポーネントを接続するには、RESTおよびSpread演算子を使用して、親が小道具の名前や種類を意識することなく、未使用の小道具を親から子に渡します。

このステップの終わりまでに、小道具が何であるかを知らなくても、ネストされたコンポーネントに小道具を提供できる親コンポーネントができあがります。 これにより、親コンポーネントが柔軟に保たれ、親を変更せずに子コンポーネントを更新できるようになります。

AnimalCardコンポーネントの作成

まず、動物のデータセットを作成します。 まず、components/Appディレクトリにあるデータセットを含むファイルを開きます。

nano src/components/App/data.js

次のデータを追加します。

src / components / App / data.js

export default [
  {
    name: 'Lion',
    scientificName: 'Panthero leo',
    size: 140,
    diet: ['meat']
  },
  {
    name: 'Gorilla',
    scientificName: 'Gorilla beringei',
    size: 205,
    diet: ['plants', 'insects']
  },
  {
    name: 'Zebra',
    scientificName: 'Equus quagga',
    size: 322,
    diet: ['plants'],
  }
]

この動物のリストは、オブジェクト配列であり、動物の名前、学名、体重、および食事が含まれています。

ファイルを保存して閉じます。

次に、AnimalCardコンポーネントのディレクトリを作成します。

mkdir src/components/AnimalCard

ディレクトで新しいファイルを開きます。

nano src/components/AnimalCard/AnimalCard.js

次に、namediet、およびsizeを小道具として使用するコンポーネントを追加し、それを表示します。

ラッパー-tutorial/src / components / AnimalCard / AnimalCard.js

import React from 'react';
import PropTypes from 'prop-types';

export default function AnimalCard({ diet, name, size }) {
  return(
    <div>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <div>{diet.join(', ')}.</div>
    </div>
  )
}

AnimalCard.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

ここでは、AnimalCard関数のパラメーターリストにある小道具を破壊し、divにデータを表示しています。 dietデータは、 join()メソッドを使用して単一の文字列としてリストされます。 各データには、データ型が正しいことを確認するために、対応するPropTypeが含まれています。

ファイルを保存して閉じます。

コンポーネントとデータが揃ったので、それらを組み合わせる必要があります。 これを行うには、コンポーネントとデータをプロジェクトのルートコンポーネントApp.jsにインポートします。

まず、コンポーネントを開きます。

nano src/components/App/App.js

そこから、データをループして、関連する小道具を含む新しいAnimalCardを返すことができます。 強調表示された行をApp.jsに追加します。

ラッパー-tutorial/src / components / App / App.js

import React from 'react';
import './App.css';

import animals from './data';
import AnimalCard from '../AnimalCard/AnimalCard';

function App() {
  return (
    <div className="wrapper">
      {animals.map(animal =>
        <AnimalCard
          diet={animal.diet}
          key={animal.name}
          name={animal.name}
          size={animal.size}
        />
      )}
    </div>
  );
}

export default App;

ファイルを保存して閉じます。

より複雑なプロジェクトで作業する場合、データは APIlocalStorage 、静的ファイルなど、より多様な場所から取得されます。 ただし、これらのそれぞれを使用するプロセスは似ています。データを変数に割り当て、データをループします。 この場合、データは静的ファイルからのものであるため、変数に直接インポートしています。

このコードでは、 .map()メソッドを使用して、animalsを反復処理し、小道具を表示します。 すべてのデータを使用する必要はないことに注意してください。 たとえば、scientificNameプロパティを明示的に渡していない。 また、Reactがマップされたデータを追跡するために使用する別のkey小道具を追加しています。 最後に、スタイリングを追加するために使用するwrapperclassNameを使用して、コードをdivでラップします。

このスタイルを追加するには、App.cssを開きます。

nano src/components/App/App.css

ボイラープレートのスタイルを削除し、flexプロパティwrapperというクラスに追加します。

prop-tutorial / src / components / App / App.css

.wrapper {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    padding: 20px;
}

これは、フレックスボックスレイアウトを使用してデータを整理し、データが整列するようにします。 paddingはブラウザウィンドウにいくらかのスペースを与え、justify-contentは要素間の余分なスペースを広げます。

ファイルを保存して終了します。 これを行うと、ブラウザが更新され、一部のデータが間隔を空けて表示されます。

詳細コンポーネントの作成

これで、データを表示する単純なコンポーネントができました。 ただし、テキストを絵文字に変換して、dietデータに少しセンスを加えたいとしましょう。 これを行うには、コンポーネントのデータを変換します。

Reactは柔軟に設計されているため、データを変換する方法を考えるときは、いくつかの異なるオプションがあります。

  • コンポーネント内に、テキストを絵文字に変換する関数を作成できます。
  • 関数を作成してコンポーネント外のファイルに保存すると、さまざまなコンポーネント間でロジックを再利用できます。
  • テキストを絵文字に変換する別のコンポーネントを作成できます。

それぞれのアプローチは、適切なユースケースに適用すれば問題なく、アプリケーションを構築するときにそれらを切り替えることができます。 時期尚早の抽象化と複雑さを回避するには、最初のオプションを使用して開始する必要があります。 ロジックを再利用したい場合は、コンポーネントとは別に関数を引き出すことができます。 3番目のオプションは、ロジックとマークアップを含む再利用可能な部分が必要な場合、またはアプリケーション全体で使用するために分離したい場合に最適です。

この場合、後でデータを追加する必要があり、マークアップと変換ロジックを組み合わせているため、新しいコンポーネントを作成します。

新しいコンポーネントはAnimalDetailsと呼ばれます。 これを作成するには、新しいディレクトリを作成します。

mkdir src/components/AnimalDetails

次に、テキストエディタでAnimalDetails.jsを開きます。

nano src/components/AnimalDetails/AnimalDetails.js

ファイル内に、dietを絵文字として表示する小さなコンポーネントを作成します。

ラッパー-チュートリアル/src/components/AnimalDetails/AnimalDetails.js

import React from 'react';
import PropTypes from 'prop-types';
import './AnimalDetails.css';

function convertFood(food) {
  switch(food) {
    case 'insects':
      return '🐜';
    case 'meat':
      return '🍖';
    case 'plants':
    default:
      return '🌱';
  }
}

export default function AnimalDetails({ diet }) {
  return(
    <div className="details">
      <h4>Details:</h4>
      <div>
        Diet: {diet.map(food => convertFood(food)).join(' ')}
      </div>
    </div>
  )
}

AnimalDetails.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
}

AnimalDetails.propTypesオブジェクトは、文字列の配列であるdietの小道具を取る関数を設定します。 次に、コンポーネント内で、コードはdietをループし、switchステートメントを使用して文字列を絵文字に変換します。

ファイルを保存して閉じます。

CSSもインポートしているので、ここで追加しましょう。

AnimalDetails.cssを開きます:

nano src/components/AnimalDetails/AnimalDetails.css

CSSを追加して、要素に境界線と余白を付け、コンポーネントの残りの部分から詳細を分離します。

ラッパー-チュートリアル/src/components/AnimalDetails/AnimalDetails.css

.details {
    border-top: gray solid 1px;
    margin: 20px 0;
}

.detailsを使用して、detailsclassNameを持つ要素にルールを一致させます。

ファイルを保存して閉じます。

新しいカスタムコンポーネントができたので、それをAnimalCardコンポーネントに追加できます。 AnimalCard.jsを開きます:

nano src/components/AnimalCard/AnimalCard.js

diet.joinステートメントを新しいAnimalDetailsコンポーネントに置き換え、強調表示された行を追加して、dietを小道具として渡します。

ラッパー-tutorial/src / components / AnimalCard / AnimalCard.js

import React from 'react';
import PropTypes from 'prop-types';
import AnimalDetails from '../AnimalDetails/AnimalDetails';

export default function AnimalCard({ diet, name, size }) {
  return(
    <div>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        diet={diet}
      />
    </div>
  )
}

AnimalCard.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

ファイルを保存すると、ブラウザに新しい詳細が表示されます。

...propsを使用してコンポーネントを介して詳細を渡す

コンポーネントは一緒にうまく機能していますが、AnimalCardにはわずかな非効率性があります。 props引数からdietを明示的に引き出していますが、データを使用していません。 代わりに、それをコンポーネントに渡します。 これについて本質的に悪いことは何もありません。実際、コミュニケーションが多すぎるという側面で誤りを犯したほうがよい場合がよくあります。 ただし、これを行うと、コードの保守がより困難になります。 新しいデータをAnimalDetailsに渡す場合は常に、小道具を渡すApp、小道具を消費するAnimalDetailsAnimalCard、これは仲介役です。

より良い方法は、AnimalCard内に未使用の小道具を集めて、それらをAnimalDetailsに直接渡すことです。 これにより、AnimalCardを変更せずに、AnimalDetailsに変更を加えることができます。 実際、AnimalCardは、AnimalDetailsに入る小道具やPropTypesについて何も知る必要はありません。

これを行うには、オブジェクトレスト演算子を使用します。 このオペレーターは、分解中に引き出されなかったアイテムを収集し、それらを新しいオブジェクトに保存します。

簡単な例を次に示します。

const dog = {
    name: 'dog',
    diet: ['meat']
}

const { name, ...props  } = dog;

この場合、変数name'dog'になり、変数props{ diet: ['meat']}になります。

これまで、すべての小道具をHTML属性であるかのように渡してきましたが、オブジェクトを使用して小道具を送信することもできます。 オブジェクトを小道具として使用するには、中括弧で囲まれたスプレッド演算子...propsを使用する必要があります。 これにより、各キーと値のペアが小道具に変更されます。

AnimalCard.jsを開きます:

nano src/components/AnimalCard/AnimalCard.js

内部で、分解されたオブジェクトからdietを削除し、代わりに残りの小道具をpropsという変数に収集します。 次に、それらの小道具をAnimalDetailsに直接渡します。

ラッパー-tutorial/src / components / AnimalCard / AnimalCard.js

import React from 'react';
import PropTypes from 'prop-types';
import AnimalDetails from '../AnimalDetails/AnimalDetails';

export default function AnimalCard({ name, size, ...props }) {
  return(
    <div>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        {...props}
      />
    </div>
  )
}

AnimalCard.propTypes = {
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

このコンポーネントではプロップを使用していないため、dietPropTypeを削除できることに注意してください。

この場合、AnimalDetailsに渡す小道具は1つだけです。 複数の小道具がある場合は、順序が重要になります。 後の小道具は前の小道具を上書きするので、優先したい小道具がある場合は、それが最後であることを確認してください。 propsオブジェクトに名前付きの値でもあるプロパティがある場合、これにより混乱が生じる可能性があります。

ファイルを保存して閉じます。 ブラウザが更新され、すべてが同じように見えます。

...propsオブジェクトがどのように柔軟性を追加するかを確認するために、AnimalCardコンポーネントを介してscientificNameAnimalDetailsに渡します。

まず、App.jsを開きます。

nano src/components/App/App.js

次に、scientificNameを小道具として渡します。

ラッパー-tutorial/src / components / App / App.js

import React from 'react';
import './App.css';

import animals from './data';
import AnimalCard from '../AnimalCard/AnimalCard';

function App() {
  return (
    <div className="wrapper">
      {animals.map(animal =>
        <AnimalCard
          diet={animal.diet}
          key={animal.name}
          name={animal.name}
          size={animal.size}
          scientificName={animal.scientificName}
        />
      )}
    </div>
  );
}

export default App;

ファイルを保存して閉じます。

AnimalCardをスキップします。 そこで変更を加える必要はありません。 次に、AnimalDetailsを開いて、新しい小道具を消費できるようにします。

nano src/components/AnimalDetails/AnimalDetails.js

新しい小道具は文字列になり、detailsリストにPropTypeを宣言する行とともに追加します。

ラッパー-チュートリアル/src/components/AnimalDetails/AnimalDetails.js

import React from 'react';
...
export default function AnimalDetails({ diet, scientificName }) {
  return(
    <div className="details">
      <h4>Details:</h4>
      <div>
        Scientific Name: {scientificName}.
      </div>
      <div>
        Diet: {diet.map(food => convertFood(food)).join(' ')}
      </div>
    </div>
  )
}

AnimalDetails.propTypes = {
  diet: PropTypes.arrayOf(PropTypes.string).isRequired,
  scientificName: PropTypes.string.isRequired,
}

ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、AnimalCardコンポーネントに変更を加えることなく新しい詳細が表示されます。

このステップでは、未知の小道具を受け取り、spread演算子を使用してネストされたコンポーネントに渡すことができる柔軟な親小道具を作成する方法を学びました。 これは、焦点を絞った責任を持つコンポーネントを作成するために必要な柔軟性を提供する一般的なパターンです。 次のステップでは、組み込みのchildrenプロップを使用して、未知のコンポーネントをプロップとして使用できるコンポーネントを作成します。

ステップ3—childrenを使用してラッパーコンポーネントを作成する

このステップでは、未知のコンポーネントのグループを小道具として受け取ることができるラッパーコンポーネントを作成します。 これにより、標準のHTMLのようなコンポーネントをネストできるようになり、再利用可能なラッパーを作成するためのパターンが得られます。これにより、共通のデザインでありながら柔軟なインテリアを必要とするさまざまなコンポーネントを作成できます。

Reactは、子コンポーネントを収集するchildrenと呼ばれる組み込みの小道具を提供します。 これを使用すると、ラッパーコンポーネントの作成が直感的で読みやすくなります。

まず、Cardという新しいコンポーネントを作成します。 これは、新しいカードコンポーネントの標準スタイルを作成するためのラッパーコンポーネントになります。

新しいディレクトリを作成します。

mkdir src/components/Card

次に、テキストエディタでCardコンポーネントを開きます。

nano src/components/Card/Card.js

次のコードを追加して、childrentitleを小道具として受け取り、それらをdivでラップするコンポーネントを作成します。

ラッパー-tutorial/src / components / Card / Card.js

import React from 'react';
import PropTypes from 'prop-types';
import './Card.css';

export default function Card({ children, title }) {
  return(
    <div className="card">
      <div className="card-details">
        <h2>{title}</h2>
      </div>
      {children}
    </div>
  )
}

Card.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.element), 
    PropTypes.element.isRequired
  ]),
  title: PropTypes.string.isRequired,
}

childrenPropTypesは新品です。 children小道具は、JSX要素またはJSX要素の配列のいずれかです。 titleは文字列です。

ファイルを保存して閉じます。

次に、スタイリングを追加します。 Card.cssを開きます:

nano src/components/Card/Card.css

カードの詳細の下に境界線と線が表示されます。

ラッパー-tutorial/src / components / Card / Card.css

.card {
    border: black solid 1px;
    margin: 10px;
    padding: 10px;
    width: 200px;
}

.card-details {
    border-bottom: gray solid 1px;
    margin-bottom: 20px;
}

ファイルを保存して閉じます。 コンポーネントができたので、それを使用する必要があります。 各AnimalCardApp.jsCardコンポーネントでラップできますが、AnimalCardという名前は、すでにCardであることを示しているため、 AnimalCard内でCardコンポーネントを使用することをお勧めします。

AnimalCardを開きます:

nano src/components/AnimalCard/AnimalCard.js

他の小道具とは異なり、childrenを明示的に渡すことはありません。 代わりに、HTMLの子要素であるかのようにJSXを含めます。 つまり、次のように、要素内にネストするだけです。

ラッパー-tutorial/src / components / AnimalCard / AnimalCard.js

import React from 'react';
import PropTypes from 'prop-types';
import Card from '../Card/Card';
import AnimalDetails from '../AnimalDetails/AnimalDetails';

export default function AnimalCard({ name, size, ...props }) {
  return(
    <Card title="Animal">
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        {...props}
      />
    </Card>
  )
}

AnimalCard.propTypes = {
  name: PropTypes.string.isRequired,
  size: PropTypes.number.isRequired,
}

Reactコンポーネントとは異なり、子として単一のルート要素を持つ必要はありません。 そのため、CardPropTypeは、要素の配列または単一の要素である可能性があると指定しました。 childrenをネストされたコンポーネントとして渡すことに加えて、カードにAnimalというタイトルを付けます。

ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、更新されたカードコンポーネントが表示されます。

これで、ネストされた子をいくつでも使用できる再利用可能なCardコンポーネントができました。 これの主な利点は、Cardを任意のコンポーネントで再利用できることです。 Plantカードを作成したい場合は、植物情報をCardコンポーネントでラップすることで作成できます。 関連付ける必要すらありません。音楽やアカウントデータなどを一覧表示するまったく異なるアプリケーションでCardコンポーネントを再利用したい場合は、それも可能です。 Cardコンポーネントは、子が何であるかを気にしません。 ラッパー要素を再利用しているだけです。この場合は、スタイル付きの境界線とタイトルです。

childrenを使用することの欠点は、子プロップのインスタンスを1つしか持てないことです。 場合によっては、コンポーネントにカスタムJSXを複数の場所に配置する必要があります。 幸い、JSXコンポーネントとReactコンポーネントを小道具として渡すことでこれを行うことができます。これについては、次のステップで説明します。

ステップ4—コンポーネントを小道具として渡す

このステップでは、Cardコンポーネントを変更して、他のコンポーネントを小道具として使用します。 これにより、コンポーネントは、ページ全体の複数の場所に不明なコンポーネントまたはJSXを表示するための最大限の柔軟性が得られます。 一度しか使用できないchildrenとは異なり、小道具と同じ数のコンポーネントを使用できるため、ラッパーコンポーネントは、標準の外観と構造を維持しながら、さまざまなニーズに適応できます。

この手順を完了すると、子コンポーネントをラップしたり、カード内の他のコンポーネントを表示したりできるコンポーネントができあがります。 このパターンは、単純な文字列や整数よりも複雑な情報を必要とするコンポーネントを作成する必要がある場合に柔軟性を提供します。

Cardコンポーネントを変更して、detailsという任意のReact要素を取得してみましょう。

まず、Cardコンポーネントを開きます。

nano src/components/Card/Card.js

次に、detailsという新しい小道具を追加し、<h2>要素の下に配置します。

ラッパー-tutorial/src / components / Card / Card.js

import React from 'react';
import PropTypes from 'prop-types';
import './Card.css';

export default function Card({ children, details, title }) {
  return(
    <div className="card">
      <div className="card-details">
        <h2>{title}</h2>
        {details}
      </div>
      {children}
    </div>
  )
}

Card.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.arrayOf(PropTypes.element), 
    PropTypes.element.isRequired
  ]),
  details: PropTypes.element,
  title: PropTypes.string.isRequired,
}

Card.defaultProps = {
  details: null,
}

この小道具はchildrenと同じタイプになりますが、オプションである必要があります。 オプションにするには、デフォルト値nullを追加します。 この場合、ユーザーが詳細を渡さなくても、コンポーネントは引き続き有効であり、余分なものは何も表示されません。

ファイルを保存して閉じます。 ページが更新され、以前と同じ画像が表示されます。

次に、AnimalCardにいくつかの詳細を追加します。 まず、AnimalCardを開きます。

nano src/components/AnimalCard/AnimalCard.js

Cardコンポーネントはすでにchildrenを使用しているため、新しいJSXコンポーネントを小道具として渡す必要があります。 これらはすべて哺乳類なので、カードに追加しますが、<em>タグで囲んで斜体にします。

ラッパー-tutorial/src / components / AnimalCard / AnimalCard.js

import React from 'react';
...

export default function AnimalCard({ name, size, ...props }) {
  return(
    <Card title="Animal" details={<em>Mammal</em>}>
      <h3>{name}</h3>
      <div>{size}kg</div>
      <AnimalDetails
        {...props}
      />
    </Card>
  )
}
...

ファイルを保存します。 これを行うと、ブラウザが更新され、Mammalというフレーズを含む更新が表示されます。

この小道具は、あらゆるサイズのJSXを使用できるため、すでに強力です。 この例では、要素を1つだけ追加しましたが、必要なだけJSXを渡すことができます。 また、JSXである必要はありません。 たとえば、複雑なマークアップがある場合は、それを小道具に直接渡したくないでしょう。 これは読みにくいでしょう。 代わりに、別のコンポーネントを作成してから、そのコンポーネントを小道具として渡すことができます。

これが機能していることを確認するには、AnimalDetailsdetails小道具に渡します。

ラッパー-tutorial/src / components / AnimalCard / AnimalCard.js

import React from 'react';
...

export default function AnimalCard({ name, size, ...props }) {
  return(
    <Card
      title="Animal"
      details={
        <AnimalDetails
          {...props}
        />
      }
    >
      <h3>{name}</h3>
      <div>{size}kg</div>
    </Card>
  )
}
...

AnimalDetailsはより複雑で、マークアップの行がいくつかあります。 detailsに直接追加すると、小道具が大幅に増えて読みにくくなります。

ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、カードの上部に詳細が表示されます。

これで、カスタムJSXを取得して複数の場所に配置できるCardコンポーネントができました。 あなたは単一の小道具に制限されていません。 必要な数の小道具に要素を渡すことができます。 これにより、柔軟なラッピングコンポーネントを作成して、他の開発者が全体的なスタイルと機能を維持しながらコンポーネントをカスタマイズできるようになります。

コンポーネントを小道具として渡すことは完璧ではありません。 読みにくく、childrenを渡すほど明確ではありませんが、柔軟性があり、コンポーネントで必要な数だけ使用できます。 最初にchildrenを使用する必要がありますが、それでも不十分な場合は、遠慮なく小道具にフォールバックしてください。

このステップでは、JSXコンポーネントとReactコンポーネントを小道具として別のコンポーネントに渡す方法を学びました。 これにより、ラッパーコンポーネントがJSXまたはコンポーネントを処理するために複数の小道具を必要とする可能性がある多くの状況を処理する柔軟性がコンポーネントに与えられます。

結論

予測可能な外観と構造を維持しながら、データを柔軟に表示できるさまざまなラッピングコンポーネントを作成しました。 未知の小道具を収集してネストされたコンポーネントに渡すことができるコンポーネントを作成しました。 また、組み込みのchildrenプロップを使用して、任意の数のネストされた要素を処理できるラッパーコンポーネントを作成しました。 最後に、JSXまたはReactコンポーネントを小道具として使用できるコンポーネントを作成し、ラッパーコンポーネントがさまざまなカスタマイズの複数のインスタンスを処理できるようにしました。

ラッパーコンポーネントを使用すると、コードの再利用と一貫性を最大化しながら、未知の状況に適応することができます。 このパターンは、ボタン、アラート、モーダル、スライドショーなど、アプリケーション全体で再利用する基本的なUI要素を作成するのに役立ちます。 あなたは何度もそれに戻っていることに気付くでしょう。

Reactのチュートリアルをもっと見たい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。