1
votes

L'état a changé mais la méthode de rendu de react ne se déclenche pas

Je suis actuellement en train d'apprendre moi-même le code et pour le moment plonger dans React.js. Je suis en train de créer une application simple qui affiche des informations sur la bière tirées de punkAPI ( https://punkapi.com/documentation/v2 ).

Dans mon application, je voulais ajouter une sorte de défilement "infini" qui extrairait plus de données de l'API lorsque l'utilisateur atteindrait le bas de la page.

À la moment, la méthode handleScroll fonctionne, et met à jour l'état du composant mais cela ne déclenche pas la méthode de rendu et je me demandais pourquoi.

Je me rends compte qu'il y a beaucoup de choses qui ne vont pas avec mon code et je prévoyez de le restructurer, ajoutez des booléens dans this.state pour vérifier s'il y a encore plus de données à charger, pour gérer les erreurs et pour ne pas déclencher la méthode handleScroll de manière aussi intensive.

Cependant, je me demande toujours pourquoi il n'y a pas de rendu même si l'état est mis à jour.

class BeerList extends Component {
    constructor() {
        super()
        this.state = {
            loading: false,
            beers: []
        }
        this.handleScroll = this.handleScroll.bind(this)
    }

    componentDidMount() {
        window.addEventListener('scroll', this.handleScroll, true);

        this.setState({
            loading: true
        })
        fetch("https://api.punkapi.com/v2/beers?per_page=12")
            .then(response => response.json())
            .then(data => {
                this.setState({
                    loading: false,
                    beers: data
                })
            })
    }

    handleScroll() {
        const checkForNewBeers = () => {
            let lastBeerCard = document.querySelector(".beer-list > div:last-child");
            let lastBeerCardOffset = lastBeerCard.offsetTop + lastBeerCard.clientHeight;
            let pageOffset = window.pageYOffset + window.innerHeight;

            if (pageOffset <= lastBeerCardOffset - 10) {
                return;
            }
            this.setState(prevState => {
                const beers = prevState.beers;
                const page = (prevState.beers.length / 12) + 1;
                fetch(`https://api.punkapi.com/v2/beers?per_page=12&page=${page}`)
                    .then(response => response.json())
                    .then(data => {
                        for (let item of data) {
                            beers.push(item);
                        }
                        console.log({
                            beers: beers
                        });
                        return {
                            beers: beers
                        }
                    });
            });
        }
        document.addEventListener("scroll", function (event) {
            checkForNewBeers();
        });
    }


    render() {
        let beerCards = []
        if (this.state.beers.length > 0) {
            beerCards = this.state.beers.map(beer => {
                return <BeerCard
                key = {
                    beer.id
                }
                img = {
                    beer.image_url
                }
                title = {
                    beer.name
                }
                description = {
                    beer.description
                }
                />
            })
        }

        return ( <
            div className = "container" >
            <
            div className = "row beer-list" > {
                beerCards
            } <
            /div> < /
            div >
        )
     }

}

export default BeerList


Ainsi, les BeerCards sont correctement ajoutées lorsque la page se charge puis lorsque vous faites défiler la console vous montre que l'état est mis à jour (beaucoup trop mais quand même). Je m'attendrais à ce que la page charge une tonne de BeerCards mais rien ne se passe. Pourquoi cela?


1 commentaires

Êtes-vous sûr que l'état est mis à jour dans handleScroll ? Dès que vous invoquez fetch qui est la dernière instruction dans setState , un indéfini serait retourné.


3 Réponses :


1
votes

Dès que vous invoquez fetch qui est la dernière instruction à l'intérieur de setState, un indéfini sera retourné. Essayez de transformer le paramètre setState en fonction asynchrone:

this.setState(async prevState => {
  const beers = prevState.beers;
  const page = prevState.beers.length / 12 + 1;
  let response = await fetch(
    `https://api.punkapi.com/v2/beers?per_page=12&page=${page}`
  );
  response = await response.json();
  for (const item of response) {
    beers.push(item);
  }
  console.log({
    beers: beers
  });
  return {
    beers: beers
  };
});


3 commentaires

Merci ça marche! Mais je ne comprends pas pourquoi il renverrait un indéfini. Je dois admettre que ma connaissance du fonctionnement d'Async dans js est encore assez limitée.


puis ne bloque pas l'exécution de la fonction appelante. Par conséquent, votre fonction retourne undefined . await d'autre part fait attendre JavaScript jusqu'à ce qu'une promesse se règle et renvoie son résultat. Donc, tout ce que vous écrivez après cela est assuré de ne s'exécuter qu'après la résolution de la promesse (y compris la fin de la fonction)


OK c'est très clair, merci beaucoup pour votre explication!



1
votes

Au lieu de renvoyer un objet à partir de votre fetch asynchrone, appelez setState dans le .then()

let beers = this.state.beers;
const page = (this.state.beers.length / 12) + 1;
fetch(`https://api.punkapi.com/v2/beers?per_page=12&page=${page}`)
    .then(response => response.json())
    .then(data => {
        for (let item of data) {
            beers.push(item);
        }

         this.setState({ beers });
    });


1 commentaires

Merci pour la réponse rapide! Cela semble bien plus fluide que ce que j'ai fait :)



0
votes

Vous êtes si proche! En ajoutant simplement le mot-clé "async" devant "prevState" dans "this.setState" et "return await" devant "fetch", l'état de votre application doit être mis à jour et déclencher un nouveau rendu comme prévu.


2 commentaires

Oui merci, quelqu'un a commenté presque les mêmes choses. Cependant, je ne comprends pas pourquoi il renverrait un undefined sans l'async. Je dois admettre que ma connaissance du fonctionnement d'Async dans js est encore assez limitée


Aucun problème! On dirait qu'Easwar l'a très bien expliqué. J'espère que cela a été utile :)