ChuChu
ChuChu

Reputation: 359

How do I apply filter through all paginated items and only display the filtered items?

When I apply filters, it only works on the first page of items. As I load more items, the filter disappears, and I have to click filter again, which is obviously not good. I want to only click Filter once and it will return all the items that match the criteria.

Right now, the API I'm working on only has a little more than 200 items. Should I just make 3 paginated API calls to download all items in the API and join them into a master array, then filter from that local master array? That way it will display every filtered item on the page.

But let's say I have an API with a much bigger database with a few thousand data points, then I don't want to load everything into the local array. I would like to know how to filter through all paginated items at once and display only the filtered items.

Here's my code:

import React, {Component} from 'react';
import axios from 'axios';
import BookList from './BookList';
import LoadingButton from './LoadingButton';
import FilterButton from './FilterButton';

    const root_api = "...";
    const books_per_page = 15;

    class HomePage extends Component{
      constructor(props){
        super(props);
        this.state ={
        books:[],
        pageNum: 1,
        isLoading: false,
        isDataFetched: false,
        isListComplete: false,
        error:''};
    }
    //get 15 items at a time
    getBooks(){
      const requestUrl = `${root_api}books?page=${
        this.state.pageNum
      }&per_page=${book_per_page}`;  
      this.setState({
        isLoading: true
      });
      axios.get(requestUrl)
      .then(res=>{
            res.data.length > 0
              ? this.setState({
                  books: [...this.state.books, ...res.data],
                  isLoading: false,
                  isDataFetched: true,
                  pageNum: this.state.pageNum + 1
                })
              : this.setState({
                  isLoading: false,
                  isDataFetched: true,
                  isListComplete: true
                });
      })

      .catch(err => {
        console.log(err);
        this.setState({

          isLoading: false,
          isDataFetched: false,
          error: err.message
        });
      });

    }

    filterBooks(){
      let newList =[];
      let currentList = [];
      currentList = this.state.books;
      newList = currentList.filter(function(book){
        return book.pages > 1200; 
      });
      this.setState({books: newList});
    }

    render(){

       return(
         <div>
         <FilterButton 
             isLoading={this.state.isLoading}
             isListComplete={this.state.isListComplete}
             clickHandler={()=>this.filterBooks()}
              />
           {this.state.books.length > 0 &&(
             <BookList books={this.state.books} />)}
           <LoadingButton
              isLoading={this.state.isLoading}
              isListComplete={this.state.isListComplete}
              clickHandler={()=>this.getBooks()}
           />
           }
         </div>
       )

}}

Upvotes: 0

Views: 3335

Answers (1)

James_F
James_F

Reputation: 449

You don't need to create a master local array just another state variable to keep track of whether or not if the user wants the books filtered:

constructor(props){
    super(props);
    this.state ={
        books:[],
        pageNum: 1,
        isLoading: false,
        isDataFetched: false,
        isListComplete: false,
        error:'',
        filtered: false // this is the new variable
    };
}

As well as change some functions:

getBooks(){
    const requestUrl = `${root_api}books?page=${
        this.state.pageNum
    }&per_page=${book_per_page}`;  
    this.setState({
        isLoading: true
    });
    axios.get(requestUrl)
    .then(res=>{
        let newState = {
            isDataFetched: true,
            isLoading: false
        }
        if (res.data.length > 0) {
            newState.pageNum = this.state.pageNum + 1
            newState.books = [
            ...this.state.books, 
            ...res.data
            ]
        } else {
            newState.isListComplete = true
        }
        this.setState(newState);
    }).catch(err => {
        console.log(err);
        this.setState({
            isLoading: false,
            isDataFetched: false,
            error: err.message
        });
    });
}

filterBooks(){
    this.setState(prevState => ({
        filtered: !prevState.filtered
    }));
}

EDIT: And change the render method:

render() {
    let bookListView
    if (this.state.books.length > 0) {
        let bookArr = this.state.books
        if (this.state.filtered) {
            bookArr = bookArr.filter(item => item.pages > 1200)
        }
        bookListView = <BookList books={bookArr} />
    }

    return(
        <div>
            <FilterButton 
                isLoading={this.state.isLoading}
                isListComplete={this.state.isListComplete}
                clickHandler={()=>this.filterBooks()}
            />
            {bookListView}
            <LoadingButton
                isLoading={this.state.isLoading}
                isListComplete={this.state.isListComplete}
                clickHandler={()=>this.getBooks()}
            />
        </div>
    )
}

EDIT: I moved the filtering part into the render method so if your user wants to unfilter their results.

Upvotes: 2

Related Questions