Compound components¶
What¶
set of components that share state create a simple, powerful declarative API
Why Compound Components¶
- Breaks things down 2. Makes things easier to change 3. need to make a change? it's clear which sub-element you should change
- Flexible! 5. re-order the inside components
Examples¶
- like
Table
andColumn
- where
Column
expects a bunch of props fromTable
- but we don't set it Explicitly
A
A
Epic React Toggle Example¶
Super flexible!
- You can change the order of the children!
- or straight up just remove any part
<Toggle>
<ToggleOn>The button is on</ToggleOn>
<ToggleButton />
<ToggleOff>The button is off</ToggleOff>
</Toggle>
React.cloneElement()
API¶
Why do we need cloneElement()
?¶
- like creating a new copy of array
- parent (which has the state) can't modify props directly
- React doesn't let you modify the
child.props
- React doesn't let you modify the
Flexible Compound Components uses React Context
- passes props to any descendant
- so we don't need
cloneElement
Passing in unexpected children¶
Errors out
Allowed List of components¶
const ALLOWED_TYPES = [ToggleOn, ToggleOff, ToggleButton];
if (ALLOWED_TYPES.includes(child.type)) {
return React.cloneElement(child, { on, toggle });
}
return child;
Blacklist/denylist: ignore HTML elements¶
more flexible
Implementation¶
return React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
id: `i-am-child-${index}`,
on,
toggle,
});
});
const ToggleOn = ({ on, children }) => on && children;
// Accepts `on` and `children` props and returns `children` if `on` is false
const ToggleOff = ({ on, children }) => !on && children;
// Accepts `on` and `toggle` props and returns the <Switch /> with those props.
const ToggleButton = ({ on, toggle }) => <Switch on={on} onClick={toggle} />;
Passing in a React fragment¶
If children is a Fragment it will be treated as a single child and not traversed.
const childrenArr = React.Children.toArray(children);
const extraElements = childrenArr.filter(
(el) => el.props.displayName === "extraElement"
);
const sectionChildren = childrenArr.filter(
(el) => el.type.displayName === "Section"
);
Flexible Compound Components¶
- instead of passing props via
cloneElement
- we pass down data via
Context
anduseContext
Last update:
2023-04-24