Reputation: 359
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
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