Tomasz Gałkowski
Tomasz Gałkowski

Reputation: 1929

How to check how many React children have a certain prop?

I have two types of components. Let's call them Outer and Inner. Imagine something like this:

<Outer>
    <h4>{this.prop.title} ({this.state.withX}/{this.state.total})</h4>
    <Inner isX/>
    <Inner isX/>
    <Inner/>
    <Inner/>
</Outer>

I have this function:

getInitialState: function() {
    return {
        total: React.Children.count(this.props.children),
        withX: /// ??? ///
    };
}

How do I get that value? I was trying to get something like this:

withX: function() {
    var counter = React.Children.forEach(this.props.children, function(child) {
         // if...
         return //something
    });
return counter;
}

But... I feel it'll get me nowhere.

Upvotes: 34

Views: 55663

Answers (2)

Chris Edwards
Chris Edwards

Reputation: 3612

React now has React.Children.count(children) method documented here https://react.dev/reference/react/Children#children-count

UPDATE: upon reflection, I'm not sure this actually answers the question, but will leave here anyway as people have already voted on it.

Upvotes: 55

Michelle Tilley
Michelle Tilley

Reputation: 159115

When you iterate over the children, you can inspect their props. For instance, using the forEach method you have above, you could do something like this:

withX: function() {
  var counter = 0;
  React.Children.forEach(this.props.children, function(child) {
    if (child.props.isX) counter++;
  });
  return counter;
}

React also provides a toArray helper that lets you do the same thing using the nice array methods JS provides:

return React.Children.toArray(this.props.children).filter(function(child) {
  return child.props.isX;
}).length;

If you're using ES6, can do this quite succinctly with an arrow function:

return React.Children.toArray(this.props.children).filter(c => c.props.isX).length;

The only catch is that, if Outer is doing the counting, then Outer also needs to render the h4. Here's a full example:

const App = React.createClass({
  render() {
    return (
      <Outer title="Things">
        <Inner isX/>
        <Inner isX/>
        <Inner/>
        <Inner/>
      </Outer>
    );
  }
});

const Outer = React.createClass({
  getInitialState() {
    return {
      total: React.Children.count(this.props.children),
      withX: this.countChildrenWithX(this.props.children)
    };
  },

  countChildrenWithX(children) {
    const { toArray } = React.Children;
    return toArray(children).filter(c => c.props.isX).length;
  },

  render() {
    return (
      <div>
        <h4>{this.props.title} ({this.state.withX}/{this.state.total})</h4>
        <hr />
        {this.props.children}
      </div>
    );
  }
});

const Inner = React.createClass({
  render() {
    return <div>Inner - withX = {String(!!this.props.isX)}</div>;
  }
});

And here's a working JS Bin to demonstrate: https://jsbin.com/xameyun/edit?js,output

Upvotes: 35

Related Questions