【React】props.children で呼べる子コンポーネントに props.children を介して props を追加する

 題ができるとクリックしたらポップアップが出てなんやかんやする、マウスを上に置いたら(hover イベントが起きたら)ある要素に色々機能を付与するコンポーネントを作る時に便利な方法になります。
 React.Children というプロパティから生えているメソッドがそれです。
React の最上位 API – React#React.Children#React.Children.map
 これを使うと props.children の中身を引っ張り出してなんやかんやできます。例えば次の様に具体的な見た目のコンポーネントを抜きにしてポップアップの出るコンポーネントを作れます。

import React, { useState } from 'react';
// @see https://v4.mui.com/components/popper/#popper
import Popper, { PopperProps } from '@material-ui/core/Popper';

export const PopUpWrapper: React.FC = (props) => {
  // ポップアップの根本の要素を格納する state
  const [popAnchor, setPopAnchor] = useState<PopperProps['anchorEl']>(null);

  // childrenを元に必要な情報を持った要素を作る
  const children4pop = React.Children.map(props.children, (child) => {
    // child が React の要素として問題ないかをチェック
    if (React.isValidElement(child)) {
      // child を元に新たな要素を生成
      // ここでは onClick をこのコンポーネント内で追加
      return React.cloneElement(
        child,
        { onClick: (e) => setPopAnchor(e.currentTarget) }
      );
    }
    return child;
  });

  return (
    <>
      {/* ↑でできあがった onClick 付きの children を描画 */}
      {children4pop}
      <Popper open={!!popAnchor} anchorEl={popAnchor}>
        popup
      </Popper>
    </>
  );
};

 この仕組みを利用したデモが次です。children 要素である div, button をクリックした時にポップアップが出る仕組みを一つのラッパーコンポーネントを用いて実現しています。

import "./styles.css";
import React, { useState } from "react";
// @see https://v4.mui.com/components/popper/#popper
import Popper, { PopperProps } from "@material-ui/core/Popper";

const PopUpWrapper: React.FC = (props) => {
  // ポップアップの根本の要素を格納する state
  const [popAnchor, setPopAnchor] = useState<PopperProps["anchorEl"]>(null);

  // childrenを元に必要な情報を持った要素を作る
  const children4pop = React.Children.map(props.children, (child) => {
    // child が React の要素として問題ないかをチェック
    if (React.isValidElement(child)) {
      // child を元に新たな要素を生成
      // ここでは onClick をこのコンポーネント内で追加
      return React.cloneElement(child, {
        onClick: (e) => setPopAnchor(e.currentTarget)
      });
    }
    return child;
  });

  return (
    <>
      {/* ↑でできあがった onClick 付きの children を描画 */}
      {children4pop}
      <Popper open={!!popAnchor} anchorEl={popAnchor}>
        <div className="pop">
          <span>popup</span>
          <button onClick={() => setPopAnchor(null)}>close</button>
        </div>
      </Popper>
    </>
  );
};

export default function App() {
  return (
    <div className="App">
      <PopUpWrapper>
        <div>Div要素</div>
      </PopUpWrapper>
      <PopUpWrapper>
        <button>button要素</button>
      </PopUpWrapper>
    </div>
  );
}
>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG