👶

【React】(親から子|子から親)の関数を呼び出す

2021/05/10に公開

親から子の関数を呼び出す

実装方法としては、子供側の参照を親から触れるようにしてやり、
参照を通じて子供側の関数を呼び出せれるようにしてやります。

子供側の実装

const ChildrenBase: React.ForwardRefRenderFunction<
  { printId: () => void },
  {
    id: string;
  }
> = ({ id }, ref) => {
  // 親から呼ばれる関数を定義
  useImperativeHandle(ref, () => ({
    printId() {
      console.log(id);
    },
  }));

  return <div>Children</div>;
};
const Children = forwardRef(ChildrenBase);

forwardRefuseImperativeHandleを使って実現します。
forwardRefでChildrenを参照し、useImperativeHandleで関数を参照に付与します。

親側の実装

const Parent: React.FC = () => {
  const childRef = useRef<{ printId: () => void }>();
  useEffect(() => {
    childRef.current?.printId();
  }, [childRef.current]);
  return <Children id={'1234567'} ref={childRef} />;
};

export default Parent;

親側は参照を通して、子供側の関数を呼び出します。

子から親の関数を呼び出す

今度は逆のパターンで、子供側から親側の関数を呼び出すパターンです。
実装方法として、今回はpropsで渡す実装ではなく、全ての子供に親の関数を呼べるようにしてやります。
ユースケースとしては Layout 等の共通のコンポーネントで、子供が使おうと思ったら Layout の関数が使える
ようなケースになるかと思います。

子供側の実装

type ChildrenDispatchProps = {
  printId?: (id: string) => void;
};

const Children: React.FC<ChildrenDispatchProps & { id: string }> = ({
  printId,
  id,
}) => {
  return <button onClick={() => printId(id)}>ボタン</button>;
};

親から渡される関数は ? 付きで定義しないとTypeエラーになります。

親側の実装

const Parent: React.FC = ({ children }) => {
  const printId = (id: string) => {
    console.log(id);
  };
  const childWithProps = React.Children.map(children, (child) => {
    if (React.isValidElement(child)) {
      const dispatch: ChildrenDispatchProps = {
        printId: printId,
      };
      return React.cloneElement(child, { ...dispatch });
    }
    return child;
  });
  return <div>{childWithProps}</div>;
};

const App: React.FC = () => {
  return (
    <Parent>
      <Children id={'1'} />
      <Children id={'2'} />
    </Parent>
  );
};

cloneElement で子供に親側で定義した関数を付与してやります。
↑のようにする事で、子供側は ChildrenDispatchProps を受け取るようにした場合、その子供は
親の関数 printId を使えるようになります。

React.cloneElementReact.isValidElement 等は以下リンク先を参照
React の最上位 API - React

参考リンク

Discussion