題ができるとクリックしたらポップアップが出てなんやかんやする、マウスを上に置いたら(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> ); }