Write Compound Components

InstructorKent C. Dodds

Share this video with your friends

Send Tweet

Compound components give more rendering control to the user. The functionality of the component stays intact while how it looks and the order of the children can be changed at will. We get this functionality by using the special React.Children.map function to map over the children given to our <Toggle/> component. We map over the children to pass the on state as a prop to its children. We move the visual pieces of the component out into function components and add them as static properties to <Toggle/>.

Gustavo Sequeira
~ 6 years ago

Hey, loving this course so far. I have a doubt about the render method, it'll be a little bit more neat if we isolate the arrow function in other method rather than having it on the render, right? Not sure if it works that way, I never use React.children.map

Thanks, Gustavo.

spencer
~ 6 years ago

Is there a git repo to this project by chance?

Kent C. Doddsinstructor
~ 6 years ago

Yep. Here it is: https://github.com/kentcdodds/advanced-react-patterns-v2/tree/egghead

spencer
~ 6 years ago

Awesome thanks Kent!

FateRiddle
~ 6 years ago

Hi, Kent, what if you define those compond components inside Toggle component, then you can get access to this.state.on and this.toggle directly, how about that approach?

Kent C. Doddsinstructor
~ 6 years ago

Give it a try and you'll learn why it wont work that way :)

FateRiddle
~ 6 years ago

Thank you for replying! @Kent C. This is the demo I wrote about the above mentioned compond components idea, and it works. https://codesandbox.io/s/0qrr0qxjzl. Any thought on if this pattern is viable?

Kent C. Doddsinstructor
~ 6 years ago

Oh yes, it does work if you change the API to a render prop. It's great ๐Ÿ‘ I've never found a use case for that myself. When doing a render prop API, they have everything they need to render whatever they like and I don't like imposing those components in them. So I've never done it myself, though I've seen other libraries adopt this pattern. I have no real problem with it and I think it could be useful for common use cases for your Component ๐Ÿ‘

Wenhan
~ 6 years ago

Great content! Why do we need a React.cloneElement here?

Thein
~ 6 years ago

Why do you want to put <Button /> at children? Off, On are status and good enough. Below is how I render in Toggle . And static and Toggle.On is little weird. Any better way without static ?

<div> {React.Children.map(this.props.children,(ctrl)=>React.cloneElement(ctrl, {on : this.state.on}))} <Switch on={this.state.on} onClick={this.toggle} /> </div>
Kent C. Doddsinstructor
~ 6 years ago

Hi Thein, That would get you to the same thing conceptually, but what if I as the user of your "reusable component" wanted to render my switch above the text? One of the goals of these patterns is to improve the flexibility and simplicity of components.

And static and Toggle.On is little weird. Any better way without static ?

"Better" is a matter of opinion. I like the static components because it communicates the relationship between the components, but if you don't like it you don't have to make it a static property, it can be a regular component just fine.

Kent C. Doddsinstructor
~ 6 years ago

Hey Wenhan, try it without using React.cloneElement and you'll figure out why it's important :)

Thein
~ 6 years ago

If will be better, if you can produce distinct ui component in each pattern. Not just on/off switch...

Kwinten Li
~ 6 years ago

Hi, thanks for the the awesome videos!

I just tried doing some console.log to figure out what is happening here.

The output is like below.

The render methods are called 3 times, the first 2 are with the same state. (This is gone if I clone the project on codesandbox)

The last render is due to the async setState triggered inside the toggle method.

Not sure why it is triggered, I havent yet toggle the button.

Object {on: false}

Render triggered

Object {on: false}

Render triggered

Toggle triggered

Object {on: true}

Render triggered

Kent C. Doddsinstructor
~ 6 years ago

Hi Kwinten,

My guess is it has to do with codesandbox. I wouldn't be too concerned about it in practice, but it's important to know that the render method can and does run many times and it's out of your control. Don't worry too much about it :)

Alex
~ 6 years ago

Okay, I think I get why we clone elements (it means we don't rely on the user of our reusable component to pass the necessary props, we do it ourselves?) but why use React.Children for the map when we can map over this.props.children directly? (I tried it and it works fine)

Kent C. Doddsinstructor
~ 6 years ago

That's a great question Alex. Try changing it to this:

    <Toggle onToggle={onToggle}>
      <Toggle.Button />
    </Toggle>
Alex
~ 6 years ago

Okay, because this.props.children is sometimes not an array (when there's only one or none of them). Is that all React.Children() is used for?

Kent C. Doddsinstructor
~ 6 years ago

That's a great question! Originally that's what I thought, but when I dove into the code I found it does quite a bit more. I never spent time on figuring out what exactly though. Start here: https://github.com/facebook/react/blob/69e2a0d732e1ca74f6dc5df9d0ddd0bf24373965/packages/react/src/ReactChildren.js#L326-L346

Kent C. Doddsinstructor
~ 6 years ago

I'll also note that preact always treats children as an array, so in downshift (because I want to support preact as well as react), we have a utility for this. The utility. Usage

Adam
~ 5 years ago

Hi, there is typo in code transcript "React.coneelement". Thanks for a great tutorial!

Kuba
~ 5 years ago

This is truly awesome! Thanks Kent!

Joel Pablo
~ 5 years ago
Joel Pablo
~ 5 years ago

ah nm

Karthik Radhakrishnan
~ 4 years ago

React.cloneElemement and React.Children is a good intro to more React's such not well known APIs. Thanks. Good way to start the course. I can see that the compound components are functional whereas the wrapper component is a class to support the static. Is it still not achievable via functional components?

Ryan
~ 4 years ago

Similar question to Karthik, how does this example translate with the now purely functional methods of writing React?

Kent C. Doddsinstructor
~ 4 years ago

https://kentcdodds.com/blog/compound-components-with-react-hooks

Karthik Radhakrishnan
~ 4 years ago

@Kent Thanks and yes I was able to try similar approach and apply that in my components in real projects. But the only issue I had was that IDE wasn't able to resolve/derive the static types of compound components and without TS/Flow more documentation was needed.

Apart from that, both render props and compound components were able to give more control over the components I created and was able to refactor existing code to add more flexibility in quick time. Thank you as usual.