sudheer singh
sudheer singh

Reputation: 922

Child component not rerendering on change of props

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

Answers (2)

thinhvo0108
thinhvo0108

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

Edgar Henriquez
Edgar Henriquez

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

Related Questions