Je ne souhaite pas transmettre les données de Firestore à mes composants. Mais la demande est asynchrone, donc je suis bloqué sur un problème lorsque ma méthode de rendu s'exécute avant d'obtenir les données de ma demande. Ainsi, lorsque le rendu s'exécute, la valeur pops est Null car l'état n'est pas encore défini. Aussi le composant pour une raison quelconque rend deux fois. Existe-t-il un meilleur moyen de gérer les trucs asynchrones?
J'ai déjà essayé de placer la requête sur Firestore à un endroit plus élevé (index.js) et de transmettre les données à l'application. J'ai essayé de faire une demande dans la méthode de rendu. J'ai essayé de placer la requête dans la fonction async et de l'appeler dans le constructeur pour y définir l'état. Même chose que l'appel de la méthode async.
class App extends Component { constructor(props) { super(props); this.giveUsername = this.giveUsername.bind(this); const userId = firebase.auth().currentUser.email; this.giveUsername(userId); } async giveUsername(userId){ let username; await firebase.firestore().collection('users').where('email', '==', userId).get().then((snapshot) =>{ snapshot.docs.forEach(doc =>{ console.log(`doc.data().username - ${doc.data().username}`); username = doc.data().username; }); }); this.setState( { userId : userId, username : username }); } render() { return ( <div className='App'> <Header username={this.state.username}/> <NewTask userId={this.state.userId}/> <TaskList userId={this.state.userId}/> </div> ); } }
3 Réponses :
Le rendu s'exécutera avant que vous n'obteniez vos données, c'est tout à fait normal et correct. Vérifier si les données sont nulles
et afficher un message de chargement ou rien ( return null
)
vous devez initialiser l ' état
dans le constructeur pour que votre composant s'affiche correctement même sans données, et ajouter une clé loading
pour savoir quand les données sont prêtes, et il le ferait être utile pour montrer un pinner
render() { return ( <div className='App'> {this.state.loading ? <span>Loading ... </span> : ( <Header username={this.state.username}/> <NewTask userId={this.state.userId}/> <TaskList userId={this.state.userId}/> ) } </div> ); }
définir le chargement
sur false
lorsque les données sont prêtes:
async giveUsername(userId){ ... this.setState( { userId : userId, username : username, loading: false }); }
Rendu:
constructor(props) { super(props); state = { userId : 0, username : '', loading: true } }
et la raison pour laquelle le composant rend twince est que l ' état
change et c'est normal.
La chose générale à comprendre lorsque vous faites des demandes est que, il est prévu qu'il n'y aura pas de données au début de votre composant et vous ne pouvez rien faire à ce sujet.
La meilleure pratique consiste à laisser l'utilisateur sachez que les données sont en cours de chargement.
async giveUsername(userId){ let username; await firebase.firestore().collection('users').where('email', '==', userId).get().then((snapshot) =>{ snapshot.docs.forEach(doc =>{ console.log(`doc.data().username - ${doc.data().username}`); username = doc.data().username; }); }); this.setState( { userId : userId, username : username, loaded: true }); }
Ne récupérez pas les requêtes dans votre contrôleur! Utilisez componentDidMount
componentDidMount(){ const userId = firebase.auth().currentUser.email; this.giveUsername(userId); }
Puis enfin dans giveUsername
constructor(){ this.state = {username: null, userId: null, loaded: false}; } render() { return ( <div className='App'> { !this.state.loaded ? <p>Loading...</p> : <Header username={this.state.username}/> <NewTask userId={this.state.userId}/> <TaskList userId={this.state.userId}/> } </div> ); }
p >