skip to Main Content

I’m probably missing something super obvious here, but I’m tearing my hear out trying to figure out why react sometimes is able to recognize data that I fetched while other other times it totally can’t. This usually happens after I either refresh the browser or turn off the localhost server and then restart. Anyone know what this happens?

Below is my code. Note that sometimes this renders perfectly, other times react will ways the "p" variable is undefined and therefore can not map, even though I don’t make a single change to the code and the data still loads ok on console log: Why does this happen?

import React, {useState, useEffect} from 'react'
import axios from './db.js'

function App() {

  const [p, setP] = useState() 

  const getParameters= async(req, res)=>{
    const response = await axios.get('/getTable/myData')
    const data =  response.data;
    console.log(data) // <--this always works.
    setP(data)
  }

  useEffect(()=>{
    getParameters()
  },[])


  return (
    <div className="App">
      {p.map(row=>(   // <---This is hit or miss.  React sometimes sees it. 
          <div>
            {row.label}
          </div>
       ))
       }
    </div>
  );
}

export default App;

4

Answers


  1. Posting because I don’t have enough reputation to comment…

    You should use conditional rendering before you access p.map(…) because your state p could be undefined, given that you’re doing an asynchronous request that may or may not be finished by the time you’re rendering your component.

    You can achieve conditional rendering by simply checking if your state p is not undefined before accessing it. F.e.: {p && p.map(…)}

    Login or Signup to reply.
  2. You should change your code at two lines:

    const [p, setP] = useState([])
    

    and

    setP(data || [])
    

    for null safety

    Login or Signup to reply.
  3. First approach, I think you can use the async & await inside the useEffect to make sure your data gets loaded first.

    Another option, react does not allow you to fetch immediately after you setState due to virtual DOM. You can also check on that.

    Another option, You can always check if the data is loaded or not using

    setP(data??[])
    
    Login or Signup to reply.
  4. Because, you’re not passing anything to the useState hook, the value of p is undefined initally.

    //                   no initial value
    const [p, setP] = useState( ) 
    

    Note that useEffect "runs after render", meaning the JSX will be be returned. In the first render, p is still undefined and has no .map method, leading to a runtime error.

    <div className="App">
      {p.map(row => ( // p is undefined here, runtime error
          <div>
            {row.label}
          </div>
        ))
        }
    </div>
    

    Then after the first render, the useEffect runs and getParameters is called in which setP(data) triggers a re-render.

    In this following render, p is now the array of data and can be mapped over in the JSX.

    You can fix this by checking if p is defined and an array before mapping over it.

    function App() {
    
      ...
    
      return (
        <div className="App">
          {p && Array.isArray(p) ? (
            p.map((row) => <div key={row.id}>{row.label}</div>)
          ) : (
            <p>No data</p>
          )}
        </div>
      );
    }
    

    And note that each child in a list should have a unique "key" prop. See Rendering Lists in the React docs.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search