From 3703b46a7d31cf772d517e3bd73593c63c2ac8ed Mon Sep 17 00:00:00 2001 From: Haz Date: Sat, 11 Sep 2021 13:13:30 +0200 Subject: [PATCH] Add Compound Components section to components CONTRIBUTING.md (#34697) --- packages/components/CONTRIBUTING.md | 58 +++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/packages/components/CONTRIBUTING.md b/packages/components/CONTRIBUTING.md index 3bed2829216a8..98e4422f853bf 100644 --- a/packages/components/CONTRIBUTING.md +++ b/packages/components/CONTRIBUTING.md @@ -35,12 +35,64 @@ function LinkButton( { href, children } ) { return ; } ``` +--> -### Composition patterns +#### Compound components -TBD — E.g. Using `children` vs custom render props vs arbitrary "data" props +When creating components that render a list of subcomponents, prefer to expose the API using the [Compound Components](https://kentcdodds.com/blog/compound-components-with-react-hooks) technique over array props like `items` or `options`: -### (Semi-)Controlled components +```jsx +// ❌ Don't: + +``` +```jsx +// ✅ Do: + + + + + +``` + +When implementing this pattern, avoid using `React.Children.map` and `React.cloneElement` to map through the children and augment them. Instead, use React Context to provide state to subcomponents and connect them: + +```jsx +// ❌ Don't: +function List ( props ) { + const [ state, setState ] = useState(); + return ( +
+ { Children.map( props.children, ( child ) => cloneElement( child, { state } ) ) ) } +
+ ); +} +``` +```jsx +// ✅ Do: +const ListContext = createContext(); + +function List( props ) { + const [ state, setState ] = useState(); + return ( + +
+ + ); +} + +function ListItem( props ) { + const state = useContext( ListContext ); + ... +} +``` + +