みかづきブログ・カスタム

基本的にはちょちょいのほいです。

Reactでchildrenにpropsを渡す 👨‍👩‍👧

f:id:kimizuka:20210416093201p:plain

基本的には、React.cloneElementを使い、propsを固定した状態のcomponentを作るのがセオリーのようです。

ja.reactjs.org

イメージ的には、Function.prototype.bindを使って引数を固定した関数を作るのに似ている気がしました。

developer.mozilla.org

実際に例を見てみましょう。

ChildElement.tsx

export default function ChildElement({ onClick }: {
  onClick: () => void
}) {
  return (
    <button onClick={ onClick }>button</button>
  )
}

まずは子コンポーネントです。
クリックするとonClickを発火します。

ParentElement.tsx

import { cloneElement } from 'react';

export default function ParentElement({ children }: {
  children: ReactElement
}) {
  const childrenWithProps = cloneElement(children, {
    onClick: () => alert('親からの指示')
  });

  return (
    <div>{ childrenWithProps }</div>
  )
}

次に親コンポーネントです。
渡されるであろう子コンポーネントのクリックイベントを上書きしてコンポーネントをクローンします。

page/index.tsx

import ParentElement from './ParentElement';
import ChildElement from './ParentElement';

export default function IndexPage() {
  return (
    <ParentElement>
      <ChildElement onClick={ () => alert('子の意思') } />
    </ParentElement>
  )
}

最後に、ParentElementとChildElementを実際に使うコンポーネントです。
インデックスページで使う想定で例を書いています。

ChildElementのクリックイベントを設定していますが、実際に配置されるのは、イベントを上書きされたクローンなので、このアラートが表示されることはありません。
なので、ボタンをクリックすると「子の意思」ではなく「親からの指示」が表示されます。

注意事項

今回の例だと、親からイベントを渡すメリットが皆無ですが、親のメソッドや親が持っているPropsを子に渡したい時に便利に使えます。

また、説明を簡単にするために端折りましたが、cloneElementの第一引数はReactElementである必要があります。
テキストノードなど、ReactElement以外を渡す可能性がある場合は内部で条件分岐を行うなど、対策が必要です。

そして、ReactElementが渡ってくる場合でも単一でなく、複数入ってくるケースを考慮しなければならない場合もあります。

今回はTypeScriptで型を指定しているので、childrenの型を指定しているので、単一のReactElementしか渡せない想定で例を作成しました。