thumbnail

【React】親コンポーネント内部のdata、methodをchildrenへ渡す方法

2022/02/14

以下のように親コンポーネントのdataやmethodをchildrenに渡したくなるときがたまに発生します。

import React from 'react'

export const Parent: React.FC = (props) => {
  const { children } = props

  const parentData = 'some parent data'

  return (
    <div>
      {/* ↓このchildrenにparentDataを渡したい... */}
      {children}
    </div>
  )
}

export default Parent

この際に使用する方法としては主に以下の2通りあります。

  • React.cloneElementを使う方法
  • childrenを関数として呼び出す方法

React.cloneElementを使う方法

ReactではcloneElementmethodを使ってchildrenを複製する際に任意のpropsを追加することができます。

親コンポーネント内部で受け取ったchildrenをcloneElementで複製、propsを追加する形でdataやmethodを渡す方法が以下になります。

import React from 'react'

const Parent: React.FC = (props) => {
  const { children } = props

  const parentData = 'some parent data'

  // isValidElementでchildrenの型判定 (Type Errorの回避も兼ねる)
  const newChildren = React.isValidElement(children)
    ? React.cloneElement(children, { parentData }) // childreのprops拡張
    : children

  return <div>{newChildren}</div>
}

type ChildProps = {
  parentData?: any
}

const Child: React.FC<ChildProps> = (props) => {
  const { parentData } = props

  return <div>{parentData}</div>
}

const ParentAndChild: React.FC = () => {
  return (
    <Parent>
      <Child />
    </Parent>
  )
}

export default ParentAndChild

childrenを関数として呼び出す方法

次に親コンポーネント内でchildrenを関数として呼び出す方法です。

import React from 'react'

type ParentProps = {
  childrenWithData?: (parentData: any) => React.ReactNode
}

const Parent: React.FC<ParentProps> = (props) => {
  const { children, childrenWithData } = props

  const parentData = 'some parent data'

  return <div>{childrenWithData ? childrenWithData(parentData) : children}</div>
}

type ChildProps = {
  parentData: any
}

const Child: React.FC<ChildProps> = (props) => {
  const { parentData } = props

  return <div>{parentData}</div>
}

const ParentAndChild: React.FC = () => {
  return (
    <Parent
      childrenWithData={
        // childにparentDataを渡す
        (parentData) => <Child parentData={parentData} />
      }
    />
  )
}

export default ParentAndChild

個人的なオススメは後者

2通り方法を紹介しましたが、 以下の理由から個人的には後者の方法をよく使用しています。

親コンポーネントからdataを渡すchildrenとそうではないchildrenをコードベースで明確に区別できる

前者の方法ではchildrenの種類に関わらず、強制的にそのProps拡張を行うので、すべてのchildrenに等しくdata、methodを渡します。

結果、どの子コンポーネントが親コンポーネントのdataを必要としているかコードベースでの判断が難しくなってしまいます。

後者の方法では親コンポーネントからdataを渡したいchildrenとそうでないchildrenを以下のようにコードベースで明確に区別することができます。

    <Parent>
      // 親コンポーネントのdataを必要としないchildren
      <ChildWithoutParentData />
    </Parent>

    <Parent
      childrenWithData={
        // 親コンポーネントのdataを必要とするchildren
        (parentData) => <Child parentData={parentData} />
      }
    />

親コンポーネントからのdataの流れがわかりやすい

前者だと暗黙的に親コンポーネントのdataやmethodを渡しているので、一見親コンポーネントのdataが子コンポーネントのPropsに渡っていないように見えてしまいます。

    <Parent>
      <Child />
    </Parent>

一方で後者の場合、明示的に親コンポーネントのdataを渡しているので、コードを見たときにそのdataが親コンポーネントから来ていることがひと目で理解できます。

    <Parent
      childrenWithData={
        (parentData) => <Child parentData={parentData} />
      }
    />
author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

Profile

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

© 2022 mitsuru takahashi