blankface
blankface

Reputation: 6347

Pass props to the first child using this.props.children

I know the way we pass props to children when using this.props.children the following way:

React.Children.map(this.props.children, (child: any, index: number) => {
    return React.cloneElement(child, {
                Visibility: this.state.visibility,
        });
});

To pass prop to only the first child I can use the index and wrap the cloneElement call in an if (index === 0), but this won't stop map from interating over all the children.

I'm wondering if there's a way I can break the loop right after index 0 is reached. Thanks!

Upvotes: 3

Views: 2567

Answers (2)

Chris
Chris

Reputation: 59531

Since you only need the the first item, there is no need to loop through them.

React.Children.toArray() will always give you an array, regardless if you have one or multiple children, which saves you some of the checks in T.J's answer.

So here is a slightly shorter (and imo cleaner) solution:

  const child = React.Children.toArray(children)[0];
  if (child) {
    return React.cloneElement(child, {Visibility: this.state.visibility});
  }
  return null;

React.Children.toArray(children)

Returns the children opaque data structure as a flat array with keys assigned to each child. Useful if you want to manipulate collections of children in your render methods, especially if you want to reorder or slice this.props.children before passing it down.

Upvotes: 4

T.J. Crowder
T.J. Crowder

Reputation: 1075337

I'm wondering if there's a way I can break the loop right after index 0 is reached. Thanks!

Don't use map at all, then, if you only want the first child.

const child = (Array.isArray(this.props.children) ? this.props.children[0] : this.props.children) || null;
return child && React.cloneElement(
    child,
    {Visibility: this.state.visibility }
);

That first bit:

const child = (Array.isArray(this.props.children) ? this.props.children[0] : this.props.children) || null;

...guards against the fact that this.props.children is only an array when there are multiple children (details); otherwise, it's the single child passed, or undefined if no children are passed (which we convert to null so we can directly return it from render).

Then the return child && guards aginst the possibility there were no children at all.

Example with all those situations:

class Example extends React.Component {
  constructor(...args) {
    super(...args);
    this.state = {
      visibility: true
    };
  }
  render() {
    const child = (Array.isArray(this.props.children) ? this.props.children[0] : this.props.children) || null;
    return child && React.cloneElement(
        child,
        {Visibility: String(this.state.visibility) }
    );
  }
}

const Child = props => <div>Visibility: {props.Visibility}</div>;

ReactDOM.render(
  <div>
    <Example />
    <Example><Child/></Example>
    <Example><Child/><Child/></Example>
  </div>,
  document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Upvotes: 4

Related Questions