0

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.

0

1 Answer 1

1

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>

Sign up to request clarification or add additional context in comments.

3 Comments

I suppose I should learn more about useEffect... Thank you, that solved my problem very simply.
No problem. useEffect basically looks for changes in state. If you pass in an empty dependency array it will run once when the component mounts. If you pass in states to that array the effect will run every time that state is updated.
I've added an additional effect to my answer to show you what I mean. When students is updated the effect handles the update and prints out the JSON of the state. @HarrySIV

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.