Consider the following block of code:
import React from 'react';
import Card from './components/Card';
import './App.css';
import Buttongrp from './components/Buttongrp';
import { useEffect, useState } from 'react';
function App() {
useEffect(() => {
fetch('https://reqres.in/api/users')
.then(response => response.json())
.then(data => { setDataArray(data); console.log(dataArray) });
}, []);
const [cardImgUrl, setCardImgUrl] = useState("https://i.pinimg.com/564x/a0/d5/c9/a0d5c9af2eee2970a6eea591fade8271.jpg")
const [firstName, setFirstName] = useState("")
const [lastName, setLastName] = useState("")
const [email, setEmail] = useState("")
const [dataArray, setDataArray] = useState([])
return (
<>
<div className="container d-flex my-3">
<div className="display-4 mx-auto d-inline">CV Screener</div>
</div>
<div className="container-fluid my-3">
<Card cardImgUrl={cardImgUrl} firstName={firstName} lastName={lastName} email={email} />
</div>
<div className="container">
{dataArray.data.map(() => return <Buttongrp />})}
</div>
</>
);
}
export default App;
What i want the page to do is, when the page loads, it should fetch the data using fetchapi , then render the rest of the components. Right now i am getting a Uncaught TypeError: Cannot read properties of undefined (reading 'map') error. I tried but am unable to fix the error.
CodePudding user response:
You can use this condition fr example:
{ dataArray?.data &&
dataArray.data.map(() => {
return <Buttongrp />
})
}
CodePudding user response:
You set dataArray
to have an empty array as default value but in the render function you access it like an object e.g. dataArray.data
.
If the data coming from the API is an object, then you should initialize the state with an empty object.
If the data coming from the API is an array, then you should omit the .data
in the render function so the .map
is invoked from the actual array.
Adding a condition like @Vedant Shah already mentioned also helps by preventing such errors in the live environment
CodePudding user response:
I always use the loading state and display a loader in such cases. I would recommend to do the following way
Use a loading state
const [isLoading, setLoading] = useState(true)
Initialise this withtrue
value, then switch tofalse
inside the response functionAdd a loading component before the actual return statement, this way, the main statement is not executed if the component is not ready
Just before the return statement, add the following
if (true === isLoading) {
return(<div> Loading, please wait... </div>)
}
return (
<>
<div className="container d-flex my-3">
<div className="display-4 mx-auto d-inline">CV Screener</div>
</div>
<div className="container-fluid my-3">
<Card cardImgUrl={cardImgUrl} firstName={firstName} lastName={lastName} email={email} />
</div>
<div className="container">
{(dataArray?.data || []).map(() => return <Buttongrp />})}
</div>
</>
);
This will guarantee that the main return statement will always contain the data.
- If the response returned from server will not contain an empty array in case of no result found, I will usually loop it the following way
(dataArray?.data || []).map()
in this case, ifdataArray
does not contain the "Truth" value, it will fallback to empty array, which will avoid the error, you can also extend the logic to make suredataArray?.data
is always an array.