J'étudie React depuis quelques jours. Dans mon projet de blog, j'utilise Axios pour obtenir des données d'API. Voici mon composant:
const SinglePost = props => {
const { post } = props;
console.log(post);
return (
<div>{post.content}</div>
);
};
Le code ci-dessus fonctionne bien, même si j'ai remarqué la première fois qu'il essaie de rendre le composant avec un objet de publication vide ({}) (en raison de la valeur par défaut dans 'useState' ). Cependant, cela pose des problèmes dans mon composant enfant car il utilise directement les propriétés d'objet «post». Par exemple: «post.content». Voici le code de mon composant 'SinglePost':
import React, { useState, useEffect } from "react";
import axios from "axios";
import { apiConstants } from "../../constants";
import SinglePost from "./SinglePost";
const PostContent = props => {
const {
match: { params }
} = props;
const [post, setPost] = useState({});
useEffect(() => {
axios
.get(apiConstants.singlePost + `${params.post_slug}`)
.then(function(response) {
setPost(response.data);
})
.finally(function() {
// always executed
});
}, []);
return (
<React.Fragment>
<div className="container">
<div className="row">
<div className="col-lg-8 col-md-10 mx-auto">
<SinglePost post={post} />
</div>
</div>
</div>
</React.Fragment>
);
};
export default PostContent;
Il renvoie une erreur non définie pour l'objet {post.content}. Pour résoudre le problème, j'ai dû utiliser quelque chose comme {post && , mais cela ne me semble pas correct. Existe-t-il une meilleure façon de gérer de tels scénarios?
3 Réponses :
Envisagez de réviser la logique de rendu du composant PostContent pour tenir compte du cas où aucune donnée de publication n'est présente lors de la requête réseau.
Vous pouvez par exemple initialiser votre publication state à null , puis mettez à jour le résultat rendu pour empêcher le composant SinglePost d'être rendu alors que post est null .
Une fois la requête réseau terminée et l'état post défini, le composant sera de nouveau rendu, ce qui entraînera le rendu de SinglePost avec le non-null post state:
const PostContent = props => {
const {
match: { params }
} = props;
const [post, setPost] = useState(null); /* Update: "Initial data" set to null */
useEffect(() => {
axios
.get(apiConstants.singlePost + `${params.post_slug}`)
.then(function(response) {
setPost(response.data);
})
.finally(function() {
// always executed
});
}, []);
return (
{ /* <React.Fragment> Not needed */ }
<div className="container">
<div className="row">
<div className="col-lg-8 col-md-10 mx-auto">
{ /* Update: If post is null, assume network request is
still in progress so rendering loading message instead
of single post component */ }
{ post === null ? <p>Loading</p> : <SinglePost post={post} /> }
</div>
</div>
</div>
);
};
export default PostContent;
Cette approche est généralement le modèle le plus simple et le plus courant pour les requêtes asynchrones et le rendu.
Il existe d'autres approches que vous voudrez peut-être envisager, comme cette approche déclarative des données récupération , ou l'utilisation de fonction de suspense Reacts pour un rendu asynchrone
Merci pour votre réponse. C'est similaire à celui que j'ai proposé. Bien sûr, le message «Chargement» est un plus du point de vue de l'interface utilisateur. Cependant, ma principale préoccupation est que (compte tenu de mon manque d'expérience dans React), est-il normal de gérer de tels cas ou est-ce que je l'aborde mal?
Oui, c'est une solution d'approche assez courante pour ces situations. Je viens de mettre à jour ma réponse avec des liens vers d'autres techniques que vous pourriez trouver intéressantes à étudier. J'espère que ça t'as aidé :-)
Vous devez faire de la valeur initiale de l'article un tableau:
const SinglePost = props => {
const { post } = props;
console.log(post);
return (
<div>
{post.map((post, key) => (
<div key={key}>{post.content}</div>
))}
</div>
);
};
et en une seule carte post à travers le tableau
import React, { useState, useEffect } from "react";
import axios from "axios";
import { apiConstants } from "../../constants";
import SinglePost from "./SinglePost";
const PostContent = props => {
const {
match: { params }
} = props;
const [post, setPost] = useState([]);
useEffect(() => {
axios
.get(apiConstants.singlePost + `${params.post_slug}`)
.then(function(response) {
setPost(response.data);
})
.finally(function() {
// always executed
});
}, []);
return (
<React.Fragment>
<div className="container">
<div className="row">
<div className="col-lg-8 col-md-10 mx-auto">
<SinglePost post={post} />
</div>
</div>
</div>
</React.Fragment>
);
};
export default PostContent;
Vous pouvez faire quelque chose comme
import React, { useState, useEffect } from "react";
import axios from "axios";
import { apiConstants } from "../../constants";
import SinglePost from "./SinglePost";
const PostContent = props => {
const {
match: { params }
} = props;
const [posts, setPosts] = useState([]);
useEffect(() => {
axios
.get(apiConstants.singlePost + `${params.post_slug}`)
.then(function(response) {
setPosts(response.data);
})
.finally(function() {
// always executed
});
}, []);
return (
<React.Fragment>
<div className="container">
<div className="row">
<div className="col-lg-8 col-md-10 mx-auto">
{this.state.posts.map(post => (<SinglePost post={post} key={post.id} />))
</div>
</div>
</div>
</React.Fragment>
);
};
export default PostContent;