ReactJS component to classify a list of books In my project, I am getting an array of objects from api, and I want to divide the array into 3 three arrays based on the value of the shelf property of each
array element which is an object. Now for doing that I am using formatBooks function inside the same component.

Later on I am passing this formatBooks function as prop to other component which calls this function.

I was hoping if there is any better by making another component and place the formatBooks inside that. I wanted to know a better and elegant way of doing things like this.

class BooksApp extends React.Component {
  state = {
    books: [],
    searchResults: [],
    newBooksFromSearch: [],
  }
  //format the books array
  formatBooks = () =&gt; {
    const currentlyReading = []
    const read = []
    const wantToRead = []
    this.state.books.forEach( item =&gt; {
      if(item.shelf === "currentlyReading" || item.shelf === "Currently Reading"){
          item.setShelf = "Currently Reading"
          currentlyReading.push(item)
      } 
      if(item.shelf === "read" || item.shelf === "Read" ){
          item.setShelf = "Read"
          read.push(item)
      }    
      if(item.shelf === "wantToRead" || item.shelf === "Want to Read"){
          item.setShelf = "Want to Read"
          wantToRead.push(item) 
      } 
    })
    return(
      {
        currentlyReading: currentlyReading,
        read: read,
        wantToRead: wantToRead
      }
    )
  }
You may start to make your code slightly less verbose (and with all the required semicolons):

const result = {
    currentlyReading: [],
    read: [],
    wantToRead: []
};

this.state.books.forEach(item =&gt; {
    if (...) {
        item.setShelf = "Currently Reading";
        result.currentlyReading.push(item);
    }
});

return result;



Then you might simplify comparison using regular expressions:

if (item.shelf.match(/^currently\s?reading$/i)) { ... }



Now you can see that you are MUTATING THE STATE without using setState(). That's an Incredibly Bad Thing&trade;. You do not give enough context to provide a specific suggestion then I'll mention few:

1) Do not mutate existing objects, create new ones:

if (item.shelf.match(/^currently\s?reading$/i) {
    result.currentlyReading.push({ ...item, setShelf: "Currently Reading" });
}


2) Perform this translation in componentDidUpdate() when you populate the books state property.

3) If you're using Redux then perform this translation in your Reducer (or Selector, if you're using them).


Re-think your design. Why do you need to duplicate an information you already have? What do you need these lists (with slightly changed values) for if they're already there in state.books? Let's imagine an hypothetical render() method:

render() {
    const { books } = this.state;

    return (
        &lt;React.Fragment&gt;
            &lt;Shelf title="Currently reading" books={books} filter="^currently\\s?reading$" /&gt;
            &lt;Shelf title="Read" books={books} filter="^read$" /&gt;
            &lt;Shelf title="Want to read" books={books} filter="^want\\s?to\\s?read$" /&gt;
        &lt;/React.Fragment&gt;
    );
}


Your separate lists are now obsolete. If you do not care about localization then regex for filter attribute might even be calculated directly (inside Shelf component!) with something like:

const filter = new RegExp(`^${this.props.title.replace(" ", "\\s?")}$`, "i");


Even if I am not doing this in my example please note that regular expression text MUST be escaped (before calling .replace()): Escape string for use in Javascript regex.


Now that you have a separate component...you may consider to move selection again outside and use memoization, simplified example (pseudo-code for memoization, pick the package you prefer):

render() {
    const { books } = this.state;

    const read = memoize((list, filter) =&gt; list.filter(book =&gt; book.shelf.match(filter)));

    return (
        &lt;React.Fragment&gt;
            &lt;Shelf title="Currently reading" books={read(books, /^read$/i)} /&gt;
        &lt;/React.Fragment&gt;
    );
}


Note that I'm NOT using the setShelf property any more in favor of Shelf.title property.