Skip to content

Instantly share code, notes, and snippets.

@rpominov
Last active September 19, 2018 12:36
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save rpominov/4a4e29afdf025efb09648c8e382c5687 to your computer and use it in GitHub Desktop.
Compound Components

My relationship with Compound Components

What are these?

Compound Component is a pattern in React when a component doesn't work by itself and needs other specific components to be its children or parents in order to operate.

Here is an introduction video https://www.youtube.com/watch?v=hEGg-3pIHlE

How do I feel about it?

I know, I'm not supposed to feel emotions about programming patterns, yadda yadda. I don't care. I hate Promises and Compound Components. Can I hate at least two things?

Why do I feel this way?

There are 3 main issues:

  1. Can't be correctly covered with type systems (PropTypes, Flow, TypeScript).
  2. Nesting doesn't work as usual. Breaks if you extract a child to another component. May break if you nest a child deeper than the first layer.
  3. It's confusing. When you look at a code that uses this pattern, it's hard to understand how does it really work. Therefore it's harder to make correct changes.

#1 and #2 Ryan describes better than I could in his another video https://www.youtube.com/watch?v=GK_rI4V4tZE . The video is in fact about new React API that may help to solve these problems. But until this API is available these two are real problems, and I don't understand how someone could consider to still use Compound Components as a public API of a library despite these two.

#3 is more controversial, but I think it's also a real problem. And it cannot be fixed using the new API.

What to do instead?

Use normal data structures instead of components:

// before

<Foo>
  <Bar baz="1" />
  <Bar baz="2" />
  <Bar baz="3" />
</Foo>

// after

<Foo items={[
  {baz: '1'},
  {baz: '2'},
  {baz: '3'},
]}>

Or, in a more complex case, when you want to allow another markup in between your special parent and child components, make it explicit that the parent communicates with special children. For example like this:

// before

<Foo>
  <div>
    <Bar baz="1" />
    <div><Bar baz="2" /></div>
  </div>
</Foo>

// after

<Foo render={context => (
  <div>
    <Bar baz="1" fooContext={context} />
    <div><Bar baz="2" fooContext={context} /></div>
  </div>
)} />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment