Je suis un débutant en React et j'utilise les hooks React. J'ai un composant de fonction qui doit afficher des informations sur la page. Je reçois et renvoie correctement le tableau comme vous le voyez, avant d'ajouter la partie jobs
et je peux y itérer avec map
et afficher ses informations avec succès, mais je souhaite également recevoir un autre tableau: location . ce tableau est basé sur le tableau
jobs
: je dois d'abord recevoir jobs
, puis obtenir son identifiant d'emplacement, puis recevoir des informations et les mettre dans mon tableau d'emplacement pour être utilisé dans ma page d'informations plus tard. le problème est que lorsque j'ajoute le tableau d'emplacement, je reçois plusieurs erreurs telles que res.json n'est pas une fonction
et ainsi de suite. voici mon code: function useJobs () {
const [jobs, setJobs] = React.useState([])
const [locations, setLocations] = React.useState([])
React.useEffect(() => {
fetch('/api/jobs/list-jobs', { headers: headers })
.then(r => r.json())
.then(setJobs)
}, [])
React.useEffect(() => {
jobs.map(job => (
axios.get(`/api/jobs/view-location/${job.location}/`, { headers: headers })
.then(res => res.json())
.then(setLocations)
))
}, [jobs], [])
return (jobs, locations)
}
export default function Jobs () {
const classes = useStyles()
const jobs = useJobs()
const locations = useJobs()
return (
<>
{jobs.map(job => (
<>
<div className={classes.root} key={job.id}>
......
<Row>
<Col style={{ color: 'black' }}>Title:{job.title} </Col>
<Col>Company Name:{job.company_name} </Col>
<Col style={{ color: 'black' }}>Internal Code:{job.internal_code} </Col>
</Row>
{locations.map(location => (
<Col key={location.id} style={{ color: 'black' }}>Location:{location.country}</Col>))
}
location
, mes informations job
s'affichaient correctement. mais lorsque je souhaite afficher les informations de localisation en fonction des informations fournies par la demande job.location
GET, je reçois ces erreurs. quelle partie est-ce que je fais mal? quelle est la bonne façon de mettre en œuvre cela?
3 Réponses :
Cette ligne ne renvoie pas les jobs
et locations
:
// A reusable JSON fetch wrapper that handles HTTP errors function fetchJSON(...args) { return fetch('/api/jobs/list-jobs', { headers: headers }) .then(r => { if (!r.ok) { throw new Error("HTTP error " + r.status) } return r.json() }); } function useJobs () { const [jobs, setJobs] = React.useState([]) const [locations, setLocations] = React.useState([]) React.useEffect(() => { fetchJSON('/api/jobs/list-jobs', { headers: headers }) .then(setJobs) }, []) React.useEffect(() => { // *** Don't use `map` as a simple iteration construct for (const job of jobs) { fetchJSON(`/api/jobs/view-location/${job.location}/`, { headers: headers }) .then(setLocations) // *** Need to handle #6 here } }, [jobs]) // *** No additional array here return [jobs, locations] } export default function Jobs () { const classes = useStyles() const [jobs, locations] = useJobs() // *** Use jobs and locations here return ( <> {jobs.map(job => ( <> <div className={classes.root} key={job.id}> ...... <Row> <Col style={{ color: 'black' }}>Title:{job.title} </Col> <Col>Company Name:{job.company_name} </Col> <Col style={{ color: 'black' }}>Internal Code:{job.internal_code} </Col> </Row> {locations.map(location => ( <Col key={location.id} style={{ color: 'black' }}>Location:{location.country}</Col>)) }
Au lieu de cela, elle évalue les jobs
, jette ce résultat, évalue les emplacements
et renvoie cette valeur.
Si vous vouliez renvoyer un tableau contenant jobs
et emplacements code>, vous pouvez soit retourner un tableau les contenant sous forme d'entrées:
React.useEffect(() => { jobs.map(job => ( axios.get(`/api/jobs/view-location/${job.location}/`, { headers: headers }) .then(res => res.json()) .then(setLocations) )) }, [jobs], []) // ââââââ^^^^ââââââââââââââââ remove
et l'utiliser comme ceci, plutôt que de faire deux appels à useJobs
:
const {jobs, locations} = useJobs();
ou renvoyez un objet les contenant comme propriétés:
return {jobs, locations};
et utilisez-le comme ceci, encore une fois plutôt que d'en faire deux appels à useJobs
:
const [jobs, locations] = useJobs();
Il y a quelques autres choses qui sautent:
Comme je l'ai mentionné dans un commentaire, votre code est la proie du footgun dans l'API fetch
: fetch
ne rejette que sur réseau em> erreur, pas erreur HTTP; vous devez vérifier cela séparément (généralement en vérifiant response.ok
). Détails sur mon petit blog anémique .
useEffect
accepte uniquement jusqu'à deux arguments, pas trois. Vous en utilisez trois ici:
return [jobs, locations];
Il semble étrange d'utiliser fetch
à un endroit mais axios
à un autre. Je suggère de normaliser l'un ou l'autre. À moins que vous n'ayez une bonne raison d'utiliser axios
, j'utiliserais probablement simplement un wrapper sur fetch
qui vérifie les erreurs HTTP et les rejets.
Je n'utilise pas axios
, mais Shubham Khatri signale des problèmes avec son utilisation dans votre code.
Vous utilisez map
comme simple itérateur pour un tableau. Ce n'est jamais approprié, map
sert à créer un nouveau tableau à partir des résultats du rappel map
. Si vous n'utilisez pas la valeur de retour de map
, utilisez plutôt forEach
(ou for-of
).
Vous effectuez un appel ajax par tâche dans une boucle, mais vous stockez ensuite les résultats dans des emplacements
. Cela signifie que les résultats de chaque appel de la boucle écrasent tous les résultats précédents. Vous vouliez probablement faire quelque chose avec tous les résultats.
En prenant tout cela ensemble:
return (jobs, locations)
... mais cela ne gère pas le # 6.
Merci pour la réponse, mais maintenant au lieu d'écrire l'emplacement réel devant le Location:
dans mon formulaire, il répète le texte `` `` Location: `` 3 fois dans mon formulaire sans rien dans devant (j'ai 3 objets de travail)
C'est comme l'emplacement, le pays n'est même pas défini
@Dorian - Avez-vous traité le problème n ° 6 ci-dessus?
Je sais que c'est le problème, mais si useEffect n'accepte que 2 paramètres, comment puis-je enregistrer mes informations dans un tableau comme je l'ai fait avec Jobs? Je suis perdu sur le numéro 6
@Dorian - Il est difficile d'aider car nous ne savons pas ce que vous essayez de faire avec les lieux. Je pense que cette question risque d'être trop large. Le mieux est de mettre en œuvre autant de choses ci-dessus que possible et d'essayer de savoir quoi faire avec les emplacements, et si vous êtes bloqué, postez une nouvelle question avec un exemple minimal reproductible de ce que vous avez fait et une explication de la façon dont vous aimeriez que emplacements
fonctionnent. Ensuite, nous pouvons nous concentrer sur cette seule question.
Une réponse à une requête axios n'a pas besoin d'être exécutée via res.json ()
. Il est requis pour une demande de récupération. La réponse axios
a également plusieurs informations et les données sont fournies avec resp.data
De plus, la dépendance useEffect est juste un argument au lieu que vous en transmettez deux
function useJobs () { const [jobs, setJobs] = React.useState([]) const [locations, setLocations] = React.useState({}) React.useEffect(() => { axios('/api/jobs/list-jobs', { headers: headers }) .then(res => setJobs(res.data)) }, []) React.useEffect(() => { for (const job of jobs) { axios(`/api/jobs/view-location/${job.location}/`, { headers: headers }) .then((data) => { setLocations(prev => ({...prev, [job.id]: res.data})) }) } }, [jobs]) return [jobs, locations] } export default function Jobs () { const classes = useStyles() const { jobs,locations} = useJobs(); return ( <> {jobs.map(job => ( <> <div className={classes.root} key={job.id}> ...... <Row> <Col style={{ color: 'black' }}>Title:{job.title} </Col> <Col>Company Name:{job.company_name} </Col> <Col style={{ color: 'black' }}>Internal Code:{job.internal_code} </Col> </Row> {locations[job.id].map(location => ( <Col key={location.id} style={{ color: 'black' }}>Location:{location.country}</Col>)) }
useJobs
renvoie à la fois les emplacements et les tâches, vous n'avez donc pas besoin de l'exécuter deux fois et non pas que vous ayez besoin de renvoyer le résultat de useJobs en tant qu'objet, c'est-à-dire avoir return ( jobs, locations )
au lieu de
return { jobs, locations }
Code complet:
React.useEffect(() => { axios('/api/jobs/list-jobs', { headers: headers }) .then(res => setJobs(res.data)) }, []) React.useEffect(() => { jobs.map(job => ( axios.get(`/api/jobs/view-location/${job.location}/`, { headers: headers }) .then(res => setLocations(prev => ({...prev, [job.id]: res.data}))) )) }, [jobs])
Veuillez vérifier mon message et me faire savoir si cela ne fonctionne pas pour vous
En plus des autres réponses, vous avez également un problème pour appeler setLocations
.
Parce que vous l'appelez de l'intérieur de la fonction de carte, vous l'appelez une fois pour chaque tâche.
Promise.all( jobs.map(job => ( axios.get(`/api/jobs/view-location/${job.location}/`, { headers: headers }) .then(response => response.data) )) ) .then(allLocations => setLocations(allLocations))
Vous dites effectivement:
setLocations('London'); setLocations('Paris');
Il est clair que si vous appelez cela deux fois, vous perdez Londres et ne persistez que l'emplacement final.
Au lieu de cela, vous vouliez stocker tous les emplacements dans un tableau, alors attendez que toutes les promesses se terminent, puis stockez-les tous ensemble.
jobs.map(job => ( axios.get(`/api/jobs/view-location/${job.location}/`, { headers: headers }) .then(setLocations) ))
Vous devez être conscient de la nature asynchrone du javascript ici. Vous appelez: const jobs = useJobs () const locations = useJobs () puis vous essayez d'afficher les résultats. Droit? Le code Javascript n'est pas bloquant, ce qui signifie que l'exécution du code se poursuit après les appels des 2 fonctions. Votre code essaiera d'afficher la réponse du serveur avant que la réponse ne soit prête. Vous devez utiliser des rappels ou des promesses pour récupérer la réponse du serveur.
Remarque: ce n'est pas le problème, mais quelque chose d'autre que j'ai remarqué dans votre code: il est en proie au footgun dans l'API
fetch
:fetch
ne rejette que sur réseau erreur, pas erreur HTTP; vous devez vérifier cela séparément (généralement en vérifiantresponse.ok
). Détails sur mon petit blog anémique .