generated at
Compound Components
2つ以上のComponentを一緒に使うことを表現する際に使う
1つの と、複数の という関係になる
利用者から見れば、どのComponent同士を一緒に使うべきかが明確になる
それら複数のComponentの中で状態を扱う時に便利
その状態を利用者に表出させずに扱える
1つのimoprtで済む


こういうやつ
jsx
<Tab> <Tab.Item>あいてむ1</Tab.Item> <Tab.Item>あいてむ2</Tab.Item> <Tab.Item>あいてむ3</Tab.Item> </Tab>


定義の仕方
こんな関数を用意して
ts
const compound = < Root extends React.FC<any>, Children extends { [key: string]: React.FC<any> }, >( root: Root, children: Children, ): Root & Children => { return Object.assign(root, children) as any; };
使う
ts
export const FormLayout = compound(FormLayoutRoot, { Group, Group2 });



<Tab> <Tab.Item> とか
<Select> <Select.Option> とか
<Menu>
<Menu.Button>
<Menu.Items>
<Menu.Item>



参考
useContextを使っている
Parent.Child という形にはなっていないが、例としてわかりやすい
useContextと相性が良い


useContextのユースケースとしてかなり良いと思うmrsekut
recoilだとglobalに影響が出る
同じ構造のものを使う時にfamilyを意識しないといけなくなる






render props patternとの違いを考える
子要素を複数置けるとか
これだけなら、listで受け取れば良いだけか
これはrender props patternでもできる
状態を共有できる
これはrender props patternでもできる
配置する場所を自由に指定できる
ここが大きな違いだと思うmrsekut






ShopifyのPoralisのStack
ts
// item.tsx export function Item({children, fill}: ItemProps) { const className = classNames(styles.Item, fill && styles['Item-fill']); return <div className={className}>{children}</div>; } // stack.tsx export const Stack = memo(function Stack({ children, vertical, spacing, distribution, alignment, wrap, }: StackProps) { const className = classNames( styles.Stack, vertical && styles.vertical, spacing && styles[variationName('spacing', spacing)], distribution && styles[variationName('distribution', distribution)], alignment && styles[variationName('alignment', alignment)], wrap === false && styles.noWrap, ); const itemMarkup = elementChildren(children).map((child, index) => { const props = {key: index}; return wrapWithComponent(child, Item, props); }); return <div className={className}>{itemMarkup}</div>; }) as NamedExoticComponent<StackProps> & { Item: typeof Item; }; Stack.Item = Item;
elementChildren
wrapWithComponent