基本的には、React.cloneElementを使い、propsを固定した状態のcomponentを作るのがセオリーのようです。
イメージ的には、Function.prototype.bindを使って引数を固定した関数を作るのに似ている気がしました。
実際に例を見てみましょう。
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しか渡せない想定で例を作成しました。