React state not updating accordingly

Viewed 56

I have a search component where the search result updates according to the search input, where if there is data returned from the API, it is rendered as a book grid, if there is no data, a message is displayed, and if the search input is empty, nothing is rendered.

My problem is that when query state updates the searchResult state does update but when I delete the search input so fast (make the search input empty), query becomes updates as an empty string but searchResult does not update according to it. What could be the problem?

Here is the code to the search component: (Note: I tried the componentDidUpdate() method and the setState() callback function but nothing worked)

import React, { Component } from "react";

// import "React Router" components
import { Link } from "react-router-dom";

// import custom components
import Book from "./Book";

// import required API
import * as BooksAPI from "../BooksAPI";

export default class BookSearch extends Component {
  state = {
    query: "",
    searchResult: [],
  };

  handleInputChange = (query) => {
    this.setState(
      {
        query: query.trim(),
      },
      () => {
        if (query) {
          console.log(query);
          BooksAPI.search(query).then((books) => {
            this.setState({ searchResult: books });
          });
        } else {
          this.setState({ searchResult: [] });
        }
      }
    );
  };

// componentDidUpdate(currentProps, currentState) {
//   if (currentState.query !== this.state.query && this.state.query) {
//     BooksAPI.search(this.state.query).then((books) => {
//       this.setState({ searchResult: books });
//     });
//   } else if (currentState.query !== this.state.query && !this.state.query) {
//     this.setState({ searchResult: [] });
//   }
// }

  render() {
    const { query, searchResult } = this.state;
    const { updateBookShelves } = this.props;
    return (
      <div className="search-books">
        <div className="search-books-bar">
          <Link to="/" className="close-search">
            Close
          </Link>
          <div className="search-books-input-wrapper">
            <input
              type="text"
              placeholder="Search by title or author"
              value={query}
              onChange={(event) => this.handleInputChange(event.target.value)}
            />
          </div>
        </div>
        <div className="search-books-results">
          <ol className="books-grid">
            { searchResult.error ? 
                <p>No results matching your search</p>
               : searchResult.map((book) => (
                  <Book
                    key={book.id}
                    book={book}
                    updateBookShelves={updateBookShelves}
                  />
                ))
              )
            ) )}
          </ol>
        </div>
      </div>
    );
  }
}
1 Answers

I am not 100% sure about this solution since it is using setState inside callback of other setState but you can give it a try.

I think you can probably need to use setTimeout before calling api for data and before checking if query exist or not we can set timeout to null so it will not call unwanted api calls.

  handleInputChange = query => {
    this.setState(
      {
        query: query.trim()
      },
      () => {
        if (this.timeout) {
          clearTimeout(this.timeout);
          this.timeout = null;
        }
        if (query) {
          this.timeout = setTimeout(() => {
            BooksAPI.search(query).then(books => {
              this.setState({ searchResult: books });
            });
          }, 800);
        } else {
          this.setState({ searchResult: [] });
        }
      }
    );
  };
Related