samedi 29 août 2020

Correct way to fetch data via API in React

I am learning javascript and React and the challenge (on Hackerrank practise) is to fetch data from an API which has multiple pages. There should be as many buttons as the number of pages and on clicking each button, the data from that particular page should show up below like: enter image description here

Here is the component that I wrote:

import React from 'react';

const url = "https://jsonmock.hackerrank.com/api/articles?page=";
const default_page = 1;

class Articles extends React.Component {
  state = {
    pageCount: 1,
    body: {},
    currentPage: 1,
    error: null,
    isLoading: false,
    data: []
  };

  componentDidMount(){
    this.setState({isLoading: true});
    fetch(url+default_page)
    .then(response => response.json())
    .then(body => this.setState({
      pageCount: body.total_pages, 
      data: body.data,
      body: body,
    }))
    .catch(err => this.setState({error: err}));
  }

  pageButtonGenerator(){
    if(this.state.body){
      let pageButtons=[];
      for(var i=1; i<=this.state.pageCount; i++){
        const id = i; //need this to use in event handler, BUT WHY DOES i NOT WORK (the value is always i=this.state.pageCount)
        pageButtons.push(<button data-testid="page-button" key={"page-button-"+i} onClick={(e) => this.buttonClickHandler(id)}>{i}</button>);
      }
      return pageButtons;
    }
    else{
      return <button data-testid="page-button" key="page-button-1">1</button>
    }
  }

  buttonClickHandler = (pageNum) => {
    // console.log(pageNum);
    this.setState({isLoading: true});
    fetch(url+pageNum)
    .then(response => response.json())
    .then(body => this.setState({
      pageCount: body.total_pages, 
      data: body.data,
      body: body,
      currentPage: pageNum
    }))
    .catch(err => this.setState({error: err}));
    // this.titlesGenerator();
  }

  titlesGenerator = () => {
    if(this.state.data){
      return this.state.data.map((element,index) => {
        if(element.title){ return <li key={"title-"+index+1} data-testid="result-row">{element.title}</li> }
        else{ return null }
      })
      // console.log(this.state.data);
      
    }
  }

  render() {
    return (
      <React.Fragment>
        <div className="pagination">
          {this.pageButtonGenerator()}
        </div>
        <ul className="results">
        {this.titlesGenerator()}
        </ul>
      </React.Fragment>
    );
  }
}

export default Articles;

Although my code passed the test cases, I am not very confident if I did it the right way. I have my doubts like:

  1. Should I be fetching all the pages in one go to avoid multiple network calls or should a call be made every time a page button is clicked?
  2. Am I generating buttons the right way (see the pageButtonGenerator)?
  3. Inside the for-loop in pageButtonGenerator, am I calling the onClick handler the right way? I was trying to directly pass the variable "i" but it was always = 6 (the exit value of the loop). I am struggling to understand why the variable i was also 6. I thought the closure would ensure that the value is always correct..
  4. How should I go about what to store in state and what should be derived and not stored?

Open to constructive criticism. Thanks

Aucun commentaire:

Enregistrer un commentaire