0

Taking a look at the code below, is there a better way of getting the count of items that contain a certain key/value pair inside of a react state?

This method seems like it could cause a bottleneck once the list that I'm going through becomes large.

Here's a simplified example of the question at hand:

class App extends React.Component {
  constructor() {
    super();
    
    this.state = {
      animals: [
        {type: 'cat'},
        {type: 'dog'},
        {type: 'cat'},
      ]
    };
  }

  render() {
    return(
      <div className="app">
        <Categories state={this.state} />
      </div>
    );
  }
}

class Categories extends React.Component {
  constructor() {
    super();

    this.countItems = this.countItems.bind(this);
  }

  countItems(type) {
    var count = 0;
  
    for(var i = 0; i < this.props.state.animals.length; i++) {
      if(this.props.state.animals[i].type === type) {
        count++;
      }
    }
    
    return count;
  }

  render() {
    return(
      <div className="categories">
        <div>Total animals: {this.props.state.animals.length}</div>
        <div>Cats: {this.countItems('cat')}</div>
        <div>Dogs: {this.countItems('dog')}</div>
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('container'));
<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>

<div id="container"></div>

2 Answers 2

1

When this is a method you would call often, then it might be useful to index your data (animals) by type, and keep that updated whenever you make changes.

For instance:

In the App constructor you would create another property animalsPerType:

  constructor() {
    super();

    this.state = {
      animals: [
        {type: 'cat'},
        {type: 'dog'},
        {type: 'cat'},
      ]
    };
    this.state.animalsPerType = this.state.animals.reduce(function(acc, animal) {
        return acc.set(animal.type, (acc.get(animal.type) || []).concat(animal));
    }, new Map());
  }

Then your countItems method becomes trivial:

  countItems(type) {
    return this.props.state.animalsPerType.get(type).length;
  }
Sign up to request clarification or add additional context in comments.

Comments

1

If you do not change the structure of your state, then you must do some sort of loop through and count by type.

A more expressive approach might be to use reduce:

countItems(type) {  
    return this.props.state.animals.reduce((acc, next) => {
        return next.type == type ? acc + 1 : acc)
    }, 0);
  }

However, if performance is a problem:

  1. You could keep the count in state and calculate once each time animals change

  2. You could split each type of animal into a separate array in state and then use length on each.

  3. Changing your state to something like this might help:

this.state = { animals: { dogs: [], cats: [] } }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.