【React】レンダープロップ

前回は各コンポーネントで共通する処理をカスタムフックとして切り出して再利用しました。

気づいているかもしれませんが、描画(UI)も同じような事をしています。
UIを共通化する方法としてレンダープロップがあります。今回はレンダープロップについて説明します。

レンダープロップとは

レンダープロップとはReactアプリケーションを実装する(プログラムを書く)際のテクニックになります。
コンポーネントへ描画(UI)ロジックをプロパティ経由で渡し、コンポーネントはそれを表示します。共通化されたUIロジックをコンポーネントで再利用する事を目的とします。

レンダープロップの例

前回作成したものを一つ一つレンダープロップを使って共通化を行っていきます。
前回は以下を参照して下さい。

共通化の準備

レンダープロップを使う前に、今回使用するコンポーネントは以下の通りです。

・Appコンポーネント
前回と同じ役割で、カスタムフックにてコーヒー及びミルク数量を保持するコンポーネント。

・CommonOrderコンポーネント
今回新しく作成するコンポーネント。数量を選択できる。

タイトルの共通化

まずはタイトルをレンダープロップを使用して共通化してみましょう。

Appコンポーネント

Appコンポーネントは以下の通りです。

import React from 'react';
import { useOrderCount } from "./hooks";
import CommonOrder from './CommonOrder';

function App() {

  // 選択されたコーヒーの数を保持するステート
  const [cofferOrderVal, funcA] = useOrderCount(1);

  // 選択されたミルクの数を保持するステート
  const [milkOrderVal, funcB] = useOrderCount(1);
  
  // コーヒーの数を選択するコンポーネントのタイトル
  const cofferOrderTitle = "注文するコーヒーの数を指定ください。";

  // ミルクの数を選択するコンポーネントのタイトル
  const milkOrderTitle = "注文するミルクの数を指定ください。";

  // 子コンポーネントを呼び出す
  return (
    <>
      <CommonOrder title={cofferOrderTitle}/>
      <CommonOrder title={milkOrderTitle}/>
    </>
  );
}

export default App;

前回との変更点は、Order、MilkOrderコンポーネントの使用をやめ、CommonOrderコンポーネントを使用しています。2つのCommonOrderコンポーネントを使用していますが、一つはコーヒーの数量を、二つ目はミルクの数量を選択する為のコンポーネントになります。また、タイトルを定義し、CommonOrderコンポーネントへプロパティ経由で渡しています。
なお、前回作成した useOrderCountフック をインポートしてますが、今は使用しません。

CommonOrderコンポーネント

CommonOrderコンポーネントは以下の通りです。

function CommonOrder({title}) {

    // ブラウザ表示処理
    return (
      <>
        <p>{title}</p>
      </>
    );
  }
  
  export default CommonOrder;

受け取ったタイトルを表示します。

結果

以下のようにタイトルがブラウザに表示されます。

これもレンダープロップを使用して得られた結果になります。

数量表示の共通化

次に、数量を表示してみましょう。

Appコンポーネント

Appコンポーネントは以下の通りです。

import React from 'react';
import { useOrderCount } from "./hooks";
import CommonOrder from './CommonOrder';

function App() {

  // 選択されたコーヒーの数を保持するステート
  const [cofferOrderVal, funcA] = useOrderCount(1);

  // 選択されたミルクの数を保持するステート
  const [milkOrderVal, funcB] = useOrderCount(1);
  
  // コーヒーの数を選択するコンポーネントのタイトル
  const cofferOrderTitle = "注文するコーヒーの数を指定ください。";

  // ミルクの数を選択するコンポーネントのタイトル
  const milkOrderTitle = "注文するミルクの数を指定ください。";

  // 子コンポーネントを呼び出す
  return (
    <>
      <CommonOrder val={cofferOrderVal} renderCount={value => ("コーヒーの数:" + value )} title={cofferOrderTitle}/>
      <CommonOrder val={milkOrderVal} renderCount={value => ("ミルクの数:" + value )} title={milkOrderTitle}/>
    </>
  );
}

export default App;

コーヒー、ミルクの数量をプロパティ(val)経由でCommonOrderコンポーネントへ渡しています。
また、数量の表示ロジックもプロパティ(renderCount)経由でCommonOrderコンポーネントへ渡しています。
今回は関数を表示ロジックとして渡しています。

なお、今回も useOrderCountフック をインポートしてますが、今は使用しません。

CommonOrderコンポーネント

CommonOrderコンポーネントは以下の通りです。

function CommonOrder({val, title, renderCount}) {

    // ブラウザ表示処理
    return (
      <>
        <p>{title}</p>
        <p>{renderCount(val)}</p>
      </>
    );
  }
  
  export default CommonOrder;

受け取った数量(val)、数量表示ロジック(renderCount)を表示します。

結果

以下のように数量がブラウザに表示されます。

これもレンダープロップを使用して得られた結果になります。

数量の選択

最後に数量を選択できるUIをレンダープロップを使用して表示してみましょう。

UIコンポーネント

今回は新たに SelectCount コンポーネントを作成します。このコンポーネントは数量を選択できるUIを提供するコンポーネントになります。
以下の通りです。

function SelectCount({setVal}) {

    // ブラウザ表示処理
    return (
        <div>
            <button onClick={setVal.incrementOrder}>プラス</button>
            <button onClick={setVal.decrementOrder}>マイナス</button>
            <button onClick={setVal.resetOrder}>リセット</button>
        </div>
    );
  }
  
  export default SelectCount;

ステート更新関数を受け取り、数量変更ボタン(プラス、マイナス、リセット)押下のタイミングで実行します。

Appコンポーネント

Appコンポーネントは以下の通りです。

import React from 'react';
import { useOrderCount } from "./hooks";
import CommonOrder from './CommonOrder';
import SelectCount from './SelectCount';

function App() {

  // 選択されたコーヒーの数を保持するステート
  const [cofferOrderVal, funcA] = useOrderCount(1);

  // 選択されたミルクの数を保持するステート
  const [milkOrderVal, funcB] = useOrderCount(1);
  
  // コーヒーの数を選択するコンポーネントのタイトル
  const cofferOrderTitle = "注文するコーヒーの数を指定ください。";

  // ミルクの数を選択するコンポーネントのタイトル
  const milkOrderTitle = "注文するミルクの数を指定ください。";

  // 子コンポーネントを呼び出す
  return (
    <>
      <CommonOrder
        val={cofferOrderVal} 
        renderCount={value => ("コーヒーの数:" + value )} 
        title={cofferOrderTitle}
        setVal={funcA}
        render={setValue => <SelectCount setVal={setValue} />}
      />
      <CommonOrder
        val={milkOrderVal}
        renderCount={value => ("ミルクの数:" + value )}
        title={milkOrderTitle}
        setVal={funcB}
        render={setValue => <SelectCount setVal={setValue} />}
      />
    </>
  );
}

export default App;

新たに作成した SelectCount コンポーネントを使用します。SelectCount コンポーネントをプロパティ(render)経由でCommonOrderコンポーネントへ渡しています。
ステート更新関数(funcA、及びfuncB)をプロパティ(setVal)経由でCommonOrderコンポーネントへ渡しています。

CommonOrderコンポーネント

CommonOrderコンポーネントは以下の通りです。

function CommonOrder({val, title, renderCount, setVal, render}) {

    // ブラウザ表示処理
    return (
      <>
        <p>{title}</p>
        {render(setVal)}
        <p>{renderCount(val)}</p>
      </>
    );
  }
  
  export default CommonOrder;

renderプロパティから数量選択UI(SelectCount)コンポーネントを表示します。また、ステート更新関数(setVal)をSelectCountコンポーネントへ渡しています。

結果

以下のように数量を選択できるボタンがブラウザに表示されます。

タイトルはプロパティ経由で文字列として、数量表示ロジックは関数とし、数量選択UIはコンポーネントとして渡しています。
このようにレンダープロップは、文字列、関数、コンポーネントをプロパティ経由で画面に表示する内容を渡し、受け取ったコンポーネントはそれを表示するという思想になります。

最後に

今回はレンダープロップの雰囲気を学ぶ為、無理矢理レンダープロップを使用しているので、この実装が効率的で正しいかはわかりません。

コメントを残す

メールアドレスが公開されることはありません。

© DeNnie.Lab All Rights Reserved.