【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ではcloneElement
methodを使って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} />
}
/>