I'm trying to make my first real React app and am pulling information from a database, updating the state to set that info as an array, and then trying to access the properties of the objects in the array.
function App() {
const [students, setStudents] = useState([]);
function fetchStudentInfo() {
fetch('https://api.hatchways.io/assessment/students')
.then(response => {
return response.json();
})
.then(data => {
const transformedStudentData = data.students.map(studentData => {
return {
imgURL: studentData.pic,
fullName: `${studentData.firstName} ${studentData.lastName}`,
email: studentData.email,
company: studentData.company,
skill: studentData.skill
}
});
setStudents(transformedStudentData);
});
}
fetchStudentInfo();
return (
<div> {console.log(students[0].fullName)}
<h1>Student Assessments</h1>
</div>
)
}
export default App;
I know I shouldn't be console.logging the info I'm trying to get, but I'm not even sure how to use the console in React to find out how to access my variables. Anyway, I get "TypeError: Cannot read properties of undefined (reading 'fullName')" as an error and it fails. I'm really trying to pass down the array as properties to be used in my components, but I've taken out code to try and simplify the problem for myself and this is where I've hit a wall.
On the first render your students state is an empty array because that's how you've initialised it. On that first render you can't access the first index (because the data hasn't been fetched, or the state updated), and you can't access a property of an element that doesn't exist yet.
So you need a condition in there that renders: 1) if students.length is zero return a loading message (for example) 2) otherwise map over the students array which you now know exists to produce a list of student names.
Here's a working example that uses useEffect to add an array to state after three seconds simulating your API fetch. You should be using useEffect like this for your fetch (with an empty dependency array) instead of calling your function directly in the component.
const { useEffect, useState } = React;
const json = '[{ "fullName": "Bob" },{ "fullName": "Karen" },{ "fullName": "Rick" }]';
function mockApi() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 3000);
});
}
function Example({ data }) {
const [ students, setStudents ] = useState([]);
useEffect(() => {
function getData() {
mockApi()
.then(json => JSON.parse(json))
.then(data => setStudents(data));
}
getData();
}, []);
// Initial log will be `[]`
// Second log will be the updated state stringified
useEffect(() => {
console.log(JSON.stringify(students));
}, [students]);
// No data, so return the loading message, or spinner
if (!students.length) return <div>Loading: wait 3 seconds</div>
// Data! `map` over it to produce the list
return (
<ul>
{students.map(student => {
return <li>{student.fullName}</li>;
})}
</ul>
);
};
ReactDOM.render(
<Example />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>