1

I want to pass down a prop from a parent component to all its children, but only if the prop isn't already set explicitly on the child--the user of the components should be able to override the pass-down behaviour easily.

This is similar to How to pass props to {this.props.children}, although with the difference that I want "fallback" rather than "override" behaviour: I want existing props on the children to take priority.

Concretely, what I want to implement is a set of components Grid, Grid.Row, Grid.Column, such that e.g.

<Grid size="big">  // A
  <Grid.Row>  // B
    <Grid.Column>  // C
      <Grid size="small">  // D
        <Grid.Column>  // E
          ...
        </Grid.Column>
      </Grid>
    </Grid.Column>
  </Grid.Row>
</Grid>

would pass on size from A to B to C, and from D to E. It's not passed on from C to D since we're explicitly providing our own size in the D element.


Approaches

I can think of a few different ways to approach this.

Define a second "fallback prop"

With a second prop (say, fallbackSize), we could set this prop unconditionally with React.cloneElement the usual way. The child component then checks size first, and if undefined it falls back to fallbackSize (and then to whatever it would otherwise fall back to, if anything).

This strikes me as an undesirable solution since it introduces a new prop that doesn't actually represent anything, and merely serves to work around the lack of a nice way to provide a "fallback".

Inspect child for prop presence

I'm not sure if this is doable, but another "approach" would be to map with React.Children.map as usual, but somehow inspect the child element for whether the prop is present or not, and if it isn't, add it with React.cloneElement the usual way.

Compared to the previous solution, this feels a lot cleaner. However, it does require a reliable way of telling what props a rendered child already has, and I'm not sure offhand how one would do that (is it enough to just inspect child.props.propName?)

Rely on the new Context API

React recently (as of mid-2018) introduced a new Context API that allows for managing state within a subtree of the React tree. This seems like an excellent use case for it, so I would argue this is the way to go.

That is, the parent would become a Provider providing the passed-down prop's value in its context, and the children would become Consumers of the same context, falling back to this if a prop isn't passed explicitly.

There are a couple concerns here too, though: it adds some amount of complexity, and I'm not sure if "passing down a single prop so that children know of it automatically" is minor enough that the Context API would be overkill. In fact, the Context API docs do provide the following notice:

Don’t use context just to avoid passing props a few levels down. Stick to cases where the same data needs to be accessed in many components at multiple levels.


In essence, I'm wondering what the most sensible and idiomatic approach would be here.

FireFly
  • 394
  • 4
  • 15
  • Have you tried using `componentWillReceiveProps(nextProps)` in children components ? It's automatically triggered when the component, well wrill receive props – Henkan Jul 24 '18 at 11:47
  • @Henkan I'm not sure it'd necessarily trigger in this case? Like, it's not like the props of an existing element changes. I suppose I'm not completely sure how `React.cloneElement` interacts with lifecycle methods, though. – FireFly Jul 24 '18 at 21:23
  • Well I think it would, if Grid is passing props, and get updated, children will also get updated and thus componentWillReceiveProps will be triggered I guess. Concerning `React.cloneElement`, I personnaly find it a bot overkill, like you are just checking property, so to me it would be better to just update the desired component instead of cloning it with new properties. – Henkan Jul 25 '18 at 06:00
  • @Henkan I'm not changing the props of Grid. Grid is always passing size="big"; Grid.Row should ideally take on size="big" "by default" ("inheriting" it from Grid), but if I explicitly pass `` I'd want that to override this default. Does that clarify? – FireFly Jul 25 '18 at 07:46
  • That is to say, the JSX/prop values could be considered static; they won't change dynamically (so I don't see when either component would receive a prop change) – FireFly Jul 25 '18 at 07:47

1 Answers1

0

You can use your second approach the code will be something like this

const renderedChildren = React.Children.map(children, (child) => {
  if (React.isValidElement(child)) {
    return React.cloneElement(child, {
       size,
       ...child.props
  }
})

the size is the fallback size if the a size prop has been passed to the child element it will overide the value passed by the parent

hope that helped