Search code examples
reactjsapisetstatereact-class-based-component

React API call is not working as intended with setState()


So here I'm fetching records by page. At mounting, I fetched page 1 and on next and prev button I'm changing the count, which is changing the page number and making a get request. But my code is not working correctly. It is giving me the result of count's previous state.

App.js

class App extends React.Component {


constructor(props) {
    super(props);
    this.state = {
      data: {},
      count: 1,
    };
  }



 handleApi = () => {
    fetch(`http://localhost:9000/accounts?page=${this.state.count}`)
      .then((res) => res.json())
      .then((res) => {
        this.setState({ data: res });
      })
      .catch((err) => console.log(err));
  };

  handlePrev = () => {
    if (this.state.count > 1) {
             this.setState((prevState) => ({
         count: prevState.count - 1
      }))
      this.handleApi();
    }
  };



handleNext = () => {
    if (this.state.count < this.state.data.total) {
      this.setState((prevState) => ({
         count: prevState.count + 1
      }))
      this.handleApi();
    }
  };

  componentDidMount() {
    this.handleApi();
  }

  render() {
    return (
      <div className="App container">
        <h1 className="mb-3 text-center">Pagination Homepage</h1>

        {Object.keys(this.state.data).length !== 0 ? (
          <ListData detail={this.state.data} />
        ) : (
          <h4 className="mb-5 text-center">No Data to Show.</h4>
        )}

        <nav aria-label="Page navigation example">
          <ul className="pagination justify-content-center mt-4">
            <li className="page-item">
              <button
                className="btn btn-outline-primary me-3"
                onClick={this.handlePrev}
              >
                Prev
              </button>
            </li>

            <li className="page-item">
              <button
                className="btn btn-outline-success"
                onClick={this.handleNext}
              >
                Next
              </button>
            </li>
          </ul>
        </nav>
      </div>
    );
  }
}

export default App;

ListData.js

export default class ListData extends Component {
  render() {
    return (
      <div className="list-outer">

        <h4 className="mb-3 text-center"> List Data</h4>
        <div className="list-head">
          <p>Name</p>
          <p>E-mail</p>
        </div>
        {this.props.detail.data &&
          this.props.detail.data.map((list, index) => (
            <div key={list._id} className="list">
             
              <p className="list-item">{list.name.toUpperCase()} </p>
              <p className="list-item"> {list.mail}</p>
            </div>
          ))}
      </div>
    );
  }
}

Console

After updating the count the API is still calling count's previous state. Here page number in Data should be equal to the count. enter image description here


Solution

  • Since setState works in an asynchronous way, after calling setState the this.state variable is not immediately changed. So if you want to perform an action immediately after setting state on a state variable and then return a result, use a callback:

      handlePrev = () => {
        if (this.state.count > 1) {
                 this.setState((prevState) => ({
             count: prevState.count - 1
          }), () => this.handleApi());
        }
      };
    
    
    
    handleNext = () => {
        if (this.state.count < this.state.data.total) {
          this.setState((prevState) => ({
             count: prevState.count + 1
          }), () => this.handleApi());
        }
      };