☑️
childrenに任意なコンポーネントしか渡せないよう縛りを
今回やること
例えばTabList
というコンポーネントのchildrenにはTab
しか渡せないようにしたい⚠️
<TabList>
<Tab val="1" />
<Tab val="2" />
<Tab val="3" />
</TabList>
下記のようにすると、TabList children must be Tab
というエラーをスルーさせよう。
<TabList>
<Tab val="1" />
<div />
</TabList>
childrenがTabじゃない場合はエラー
※無関係なコードは省いている最低限のものです
import { Tab } from './Tab';
type TabProps = ComponentProps<typeof Tab>;
type TabReactElement = ReactElement<TabProps>;
interface Props {
children: TabReactElement | Array<TabReactElement>;
}
export const TabList: FC<Props> = ({ children }) => {
// 下記がchildrenがTabでない場合はエラーを出す記述
// 開発環境のみチェックや、記述場所工夫してもよさそう
Children.forEach(children, (child) => {
if (child.type !== Tab) {
throw new Error('TabList children must be Tab');
}
});
return (
<ul role="tablist">
{children}
</ul>
);
開発時に気付ければいいので、isDevなどだったらチェックするようにしても良さそうです。
React.Childrenを覗く
Children
は下記のようになっており、mapなどのプロパティも存在するので、childrenのpropsを加工したりもできます。
// Sync with `ReactChildren` until `ReactChildren` is removed.
const Children: {
map<T, C>(children: C | ReadonlyArray<C>, fn: (child: C, index: number) => T):
C extends null | undefined ? C : Array<Exclude<T, boolean | null | undefined>>;
forEach<C>(children: C | ReadonlyArray<C>, fn: (child: C, index: number) => void): void;
count(children: any): number;
only<C>(children: C): C extends any[] ? never : C;
toArray(children: ReactNode | ReactNode[]): Array<Exclude<ReactNode, boolean | null | undefined>>;
};
下記は、Tab
のonClick
を加工する例です。
const tabs = Children.map(children, (child) => {
return cloneElement(child, {
onClick: handleTabClick.bind(null, child.value),
isSelected: child.props.value === selectedTab,
});
});
デザインシステムなどで、自前のコンポーネントだけchildrenに渡せるようにしたい!だったり、コーディング規約だと守りきれないと思うので、コードでチェックできると便利ですね✨
Discussion