The Limits of using Element Composition for Configuration of Libraries

(Thariq Shihipar) #1

One pattern I have been growing fond of is using composition of react elements to configure your library, for example a recharts graph might look like this:

  margin={{ top: 5, right: 20, left: 10, bottom: 5 }}
  <XAxis dataKey="name" />
  <Tooltip />
  <CartesianGrid stroke="#f5f5f5" />
  <Line type="monotone" dataKey="uv" stroke="#ff7300" yAxisId={0} />
  <Line type="monotone" dataKey="pv" stroke="#387908" yAxisId={1} />

The Tooltip, CartesianGrid components, etc. are something that could be done via props, but using children as composition has a certain elegance to it. In this case however, each component is directly rendering DOM elements as well.

React Router has a similar idea, but it doesn’t actually render things.

    <Route exact path="/invoices/dashboard" component={Dashboard}/>
    <Route path="/invoices/:id" component={Invoice}/>
    <Redirect from="/invoices" to="/invoices/dashboard"/>

I’m working on a configurable editor library and the idea of using composition heavily is very appealing. I want to get your thoughts on when you should use element composition and when it should just be props.

It seems like the pros of composition are:

  • Code Splitting & Separation - Including config in props tends to consolidate all the code into one code path, whereas using composition makes it easy to isolate code into components that can be imported or not imported.
  • Developer UX - Developers are familiar with JSX and props as a config and it feels a little bit nicer to write than a big config props.
  • Lifecycle Uses - Makes it natural to respond to the config changing because your component was unmounted, versus watching prop changes closely.

What I’m trying to get your opinion on is, when should you use composition for these cases? My initial thought was you should only use it when the elements are also rendering information somehow (like in recharts) but react router is a good example of using composition only for configuration.

(Sophie Alpert) #2

Personally, I find the pattern most compelling when each child element is optional and does correspond to something on screen, like your chart example. The components there make sense, but having other configuration such as <Size width={400} height={400} /> wouldn’t (IMO) since they’re not additive in the same way.

React Router’s pattern strays a bit from that and I think you could equally well represent it like

<Switch routes={[
  {path: '/invoices/dashboard', exact: true, component: Dashboard},
]} />

But people seem to enjoy the API they have too. There’s no hard rule. :slight_smile:

(Thariq Shihipar) #3

I agree the optionalness is an important benefit, I’m debating about the need for it to render on screen.

<Size width={400} height={400} /> of course doesn’t make sense, but I think because it’s just adding configuration and not code. If there was some meaningful code, like maybe a dynamic sizing library that could be optionally added, then maybe it could make sense as a prop.