6 votes

React setState ne se réaffiche pas après avoir récupéré des données

Ce code fonctionne si les données ont déjà été récupérées.
Mais il ne fonctionne pas si je rafraîchis la page et ne restitue pas l'élément.
J'utilise également Next JS si cela vaut la peine d'être mentionné.

class Books extends Component {
    constructor(props) {
        super(props);
        this.state = {
            book: []
        }
        this.renderBooks= this.renderBooks.bind(this);
    }

    renderBooks() {
        let item;
        let items = [];
        return new Promise((resolve, reject) => {
            this.props.ids.forEach(address => {
                firebase.database().ref(`/books/${address}`)
                    .on('value', snap => {
                        item = snap.val();
                    });
                items.push(item);
            })
            resolve(items);
        });
    }

    async componentDidMount() {
        try {
            let res = [];
            res = await this.renderBooks();
            console.log(res);
            this.setState({ book: res });
        } catch (error) {
            console.log(error);
            this.setState(prevState => {
                return { book: 'err' }
              });
        }
    }

    render() {
        return (
            <div>
                { <List grid={{ gutter: 16 }}
                        dataSource={ this.state.book }
                        renderItem={ item => (
                            <List.Item>
                                <Card title={ !!item && item.title }>
                                    ...Book content...
                                </Card>
                            </List.Item>
                        )} />
                }
            </div>
        );
    }
}

export default Books; 

Y a-t-il quelque chose à savoir sur setState et la récupération de données que j'ai manqué ici ?
PS. Edition d'un constructeur pour le livre : [].

2voto

TLP Points 708

Vous êtes en train d'initialiser this.state.book avec une promesse. Essayez de la fixer à null au lieu de cela :

this.state = {
    book: null
}

2voto

sertsedat Points 2138

Vous ne pouvez pas initialiser book avec une promesse. Au lieu de cela, vous pouvez avoir une solution comme ci-dessous.

Ajouter un rendu conditionnel à votre render afin qu'il sache quand rendre book . Vous n'avez pas non plus besoin de renvoyer new Promise dans ce cas.

class Books extends Component {
    constructor(props) {
        super(props);
        this.state = { books: null }
    }

    componentDidMount() {
        this.renderBooks()
    }

    renderBooks() {
        this.props.ids.forEach(address => {
            firebase.database().ref(`/books/${address}`)
             .on('value', snap => {
                this.setState({books: [...(this.state.books || []), snap.val()] });
            });
        });
    }

    render() {
        return (
           this.state.books ? 
            <div>
                { <List grid={{ gutter: 16 }}
                        dataSource={ this.state.books }
                        renderItem={ item => (
                            <List.Item>
                                <Card title={ !!item && item.title }>
                                    ...Book content...
                                </Card>
                            </List.Item>
                        )} />
                }
            </div> 
           : 'Initializing'
        );
    }
}

export default Books;

Les promesses sont essentiellement des fonctions asynchrones qui sont résolues le moment venu.

Ainsi, lorsque vous le faites

 var item, items = []; // <---- Step 1
 this.props.ids.forEach(address => {
     firebase.database().ref(`/books/${address}`)
         .on('value', snap => {
             item = snap.val(); // <--- Step 3
         });
     });
     items.push(item); // <----- Step 2
 });

Les étapes sont les suivantes. Vous faisiez donc items.push(item) avant que l'article ne se voie attribuer une nouvelle valeur qui est snap.val() . Et cela fait item undefined .

Je pense que le deuxième résultat que vous obtenez est dû à la mise en cache. Si la connexion internet est TELLEMENT RAPIDE Step 3 pourrait être antérieure à Step 2 mais c'est une mauvaise supposition. C'est pourquoi, la deuxième fois, le résultat est correct.

Dans le cas de cette réponse, au lieu d'avoir un items le tableau snap.val() est ajouté à this.state.books. Mais cela le rend un peu lourd. Car à chaque fois qu'une requête on('value') est appelé, le setState sera déclenchée et le composant sera rendu à nouveau. S'il y avait 1000 identifiants, l'état changerait 1000 fois.

C'est pourquoi, au lieu d'obtenir les données une par une, je vous suggère d'obtenir toutes les données en une seule fois. Essayez de googler quelque chose comme 'retrieve multiple data from firebase javascript' . Malheureusement, je ne connais pas grand-chose à firebase et je ne peux donc pas vous aider.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X