Reputation: 922
Here is the parent component :
state = {
books: undefined
}
componentDidMount() {
BooksAPI.getAll().then(books => {
this.setState({books},this.filterBooks);
});
}
filterBooks() {
this.currentlyReading = this.state.books.filter((book) => book.shelf === 'currentlyReading');
this.read = this.state.filter((book) => book.shelf === 'read');
this.wantToRead = this.state.filter((book) => book.shelf === 'wantToRead');
}
render() {
return (
<div className="app">
<Route path="/search" component={SearchBook} />
<Route exact
path="/"
render={() => <BookScreen
currentlyReading={this.currentlyReading}
read={this.read}
wantToRead={this.wantToRead}
/>} />
</div>
)
}
I expect that the props will change for BookScreen
component after filterBooks is called and the component should rerender, but it doesn't. What am I doing wrong?
Upvotes: 1
Views: 1425
Reputation: 2242
In my opinion, the best way for now, which still follows React's way is: after having books
data, call 1 function only, and this function will process everything and update state after finishing (we pass the books
object as parameter), like this:
componentDidMount() {
BooksAPI.getAll().then(books => {
this.filterBooks({books});
});
}
filterBooks = (books) => {
this.currentlyReading = books.filter((book) => book.shelf === 'currentlyReading');
this.read = books.filter((book) => book.shelf === 'read');
this.wantToRead = books.filter((book) => book.shelf === 'wantToRead');
this.setState({ books: books });
}
If you have any error, feel free to post here then we can get through together!
========
Added explanation on why the author's original code doesn't work:
Based on my little experience with React and JS:
When a new state is set, it may take time (maybe 100-300ms, that is why you execute the original this.filterBooks
using the syntax this.setState({books},this.filterBooks)
=> This seems to be right, which means after a new state of books
is already set, you can access it the filterBooks
function.)
HOWEVER: after new state of books
is set, the page will be re-rendered, and filterBooks
will be executed (perhaps at the same time => not sure which one ends first, so let's say for example, this.currentlyReading
is still undefined in render()
if render()
happens first, before the result of filterBooks
is completely set!
In other words, React is Javascript, and Javascript's asynchronization is troublesome!
Upvotes: 1
Reputation: 1383
You can try to do this, Just update the books state like this:
componentDidMount() {
BooksAPI.getAll().then(books => this.setState({books}));
}
This will cause a re-render. But since you're not using the state directly to populate your child component, we need to call the filterBooks()
inside the render()
method.
render() {
this.filterBooks()
return (
<div className="app">
<Route path="/search" component={SearchBook} />
<Route exact
path="/"
render={() => <BookScreen
currentlyReading={this.currentlyReading}
read={this.read}
wantToRead={this.wantToRead}
/>} />
</div>
)
}
The call to that method will update the data that you pass as props to your child component.
Upvotes: 1