🎨

[Chakra UI] MenuButton に as で指定したコンポーネントの props を使えるようにする

2022/11/08に公開

こんにちは、よしお (@yoshio__25) です。

Chakra UI の Menu を使うとき、ドキュメントには以下のような使い方が書かれています。

<Menu>
  <MenuButton
    as={ Button }
    rightIcon={ <ChevronDownIcon /> }
  >
    Actions
  </MenuButton>
  <MenuList>
    <MenuItem>Download</MenuItem>
    <MenuItem>Create a Copy</MenuItem>
    <MenuItem>Mark as Draft</MenuItem>
    <MenuItem>Delete</MenuItem>
    <MenuItem>Attend a Workshop</MenuItem>
  </MenuList>
</Menu>

しかし実際にエディタ上で入力していくと、補完が効かず不便を強いられることがありました。

Chakra UI でも過去に Issue で取り上げられています。
https://github.com/chakra-ui/chakra-ui/issues/231

そこで今回は as にコンポーネントを指定したときに、そのコンポーネントの props の補完が効くようにする方法を紹介します。

事前準備

直接 Chakra UI の型定義を編集するわけにはいかないので、まずは中間層を実装します。

import {
  MenuButton as ChakraMenuButton,
  MenuButtonProps as ChakraMenuButtonProps,
} from '@chakra-ui/react';

type Props = ChakraMenuButtonProps;

const MenuButton = function MenuButton(props: Props) {
  const {
    ...other
  } = props;

  return (
    <ChakraMenuButton
      { ...other }
    />
  );
};

export default MenuButton;

しかし、これではまだ状況は何も変わりません。

解決方法

そこで補完が効くように as のコンポーネントの props の型定義を抽出して、この中間層のコンポーネントに仕込んでいきます。
props の型定義の抽出は React の ComponentProps が利用できます。

  import {
    MenuButton as ChakraMenuButton,
    MenuButtonProps as ChakraMenuButtonProps,
  } from '@chakra-ui/react';
+ import {
+   ComponentProps,
+   ElementType,
+ } from 'react';
  
- type Props = ChakraMenuButtonProps;
+ type Props<C extends ElementType<any>> =
+   & ChakraMenuButtonProps
+   & ComponentProps<C>;
  
- const MenuButton = function MenuButton(props: Props) {
+ const MenuButton = function MenuButton<C extends ElementType<any>>(props: Props<C>) {
    const {
      ...other
    } = props;
  
    return (
      <ChakraMenuButton
        { ...other }
      />
    );
  };
  
  export default MenuButton;

そして MenuButton はこの中間層のコンポーネントを利用すると、以下のように補完が効かせられるようになります。

まとめ

Chakra UI の MenuButton ですが、そのまま利用しても as で指定したコンポーネントの props の補完が効きませんでした。そこで中間層のコンポーネントを作成し、コンポーネントの props を抽出するようにすることで、補完を効かせられるようにできます。

Discussion