TransWikia.com

Is it an anti-pattern on React to search for children with a specific element type?

Software Engineering Asked by Christopher Francisco on December 19, 2021

Context

When designing the API of a React component, you may decide to receive props in a more semantical way. Here’s a simple example

<Modal headerTitle="foo" />

vs

<Modal>
  <Header>Foo</Header>
</Modal

A more complex example would be this one, where we have a table and we need to set responsiveness:

const hideOn = { email: 'smDown'}
const data = [{ name: 'jonh', email: '[email protected]' }]
<Table hideOn={hideOn} data={data} />

vs

<Table>
  <Header>
    <HeaderCell>Name</HeaderCell>
    <HeaderCell hideOn="smDown">Email<HeaderCell>
  <Header>
  <Row>
    <Cell>John</Cell>
    <Cell>[email protected]</Cell>
  </Row>
</Table>

In both scenarios, I’d strongly argue the second design brings more benefits (but not going to discuss that here).

Pattern

Following the example above, in order to get the data for responsiveness of each of the tables Cell, Table need to run a children.find(child => child.type === HeaderCell).props.hideOn and pass the value to the Cell through cloneElement.

Another example would be making a component API strict so that only certain components can be passed. In this example, the hacked div won’t be rendered at all:

<Table>
  <Header>
    <div style={{backgroundImage: './foo.png', top: 0, position: 'absolute' }}> 
      Hacked div. Programmer was lazy to figure how to put a background 
      image in the header.
  
      However, this hacked div won't get rendered
    </div>
  <Header>My Modal</Header>
  ...
</Table>

In this example, Table can run a children.find(child => child.type === to find only valid children (Header and Row).

Problem definition

What if we needed to do some decoration or just DRYing up the code, like the following:

<Table>
  <SharedHeaders />
  <Row>
    <Cell>John</Cell>
    <Cell>[email protected]</Cell>
  </Row>
</Table>

At this point, we see 2 problems:

  1. Table is no longer able to find the hideOn prop, because it doesn’t have a Header within its children.
  2. Assuming Header was one of Table valid children, SharedHeader is definitely not one of them.

This leaves me wondering whether the approach itself is fundamentally flawed, and I’m looking at the solution from the incorrect perspective.

I realize there are different options to solve the use case above (such as using the Context API), but I have other use cases where we work by finding a child with specific type, so I want to focus the question specifically on that practice, and its take on composition.

Is it an anti-pattern, when doing composition, for a component to search for a component with a specific type within its children in order to work?

One Answer

It’s not a big issue to use cloneElement on structured, nested data. It’s not surprising if td elements renders differently compared to th. If you want to be explicit, you can always use the render props pattern. As an example, you can pass refs for calling imperative methods from the parent, or pass a callback to indicate that a child has changed. Something like this will suffice:

<Table rowCount={3} {...props}>
  {({onHeaderChange, onRowChangeCallbackList}) => (
    <Header onChange={onHeaderChange} />
    // render rows
  )}
</Table>

Answered by tonychow1997 on December 19, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP