Closed1

React で Vue の slot っぽいやつの実装

前田みお前田みお
import type { FC, ReactElement, ReactNode } from 'react';

type Props = {
  children: ReactElement[] | ReactElement | undefined;
};

type SlotProps = { children: ReactNode };
type SlotComponent = FC<SlotProps>;

const HeaderSlot: SlotComponent = () => null;
const BodySlot: SlotComponent = () => null;
const FooterSlot: SlotComponent = () => null;

type Slot = ReactElement<SlotProps>;

const _Layout: FC<Props> = ({ children }) => {
  const getSlot = (
    _children: Slot[] | Slot | undefined,
    slot: SlotComponent,
  ): ReactNode => {
    if (Array.isArray(_children))
      return _children.find((child) => child.type === slot)?.props.children;
    if (_children?.type === slot) return _children.props.children;

    return undefined;
  };

  const header = getSlot(children, HeaderSlot);
  const body = getSlot(children, BodySlot);
  const footer = getSlot(children, FooterSlot);

  return (
    <div>
      <header>{header}</header>

      <div>{body}</div>

      <footer>{footer}</footer>
    </div>
  );
};

export const Layout = Object.assign(_Layout, {
  Header: HeaderSlot,
  Body: BodySlot,
  Footer: FooterSlot,
});
const Example = () => (
  <Layout>
    <Layout.Header>Header</Layout.Header>
    <Layout.Body>Body</Layout.Body>
    <Layout.Footer>Footer</Layout.Footer>
  </Layout>
);

/*
<div>
  <header>Header</header>

  <div>Body</div>

  <footer>Footer</footer>
</div>
*/
このスクラップは2022/08/07にクローズされました