0

I'm using React and I'm trying to map the result of a GET request to the db to display some tabs in my page.

This is my GET request:

useEffect(async ()=>{
  const a = await getCategories();
  setCategories(a)
    
},[])
  
const [categories, setCategories] = useState()

I then store the returned array in my functional component state with useEffect hook:

useEffect(async ()=>{
  const a = await getCategories();
  setCategories(a)

},[])
  
const [categories, setCategories] = useState()

At the end, in the render I try to map through the state array like this:

<Nav variant="tabs" >
  {categories.map((category, index) => (
    <Nav.Item key={index}>
      <Nav.Link
      eventKey={`link-${index}`}
      onClick={() => setCategory(category.category)}
      >
        {category}
      </Nav.Link>
    </Nav.Item>
  ))}
</Nav>

I'm getting a TypeError: Cannot read property 'map' of undefined

I think that's because it runs the map before i have the array stored in the state but i have no idea how to solve it.

4 Answers4

3

You can set the default value for categories using an empty array:

const [categories, setCategories] = useState([])

You can also optionally add a defensive conditional mapping by checking if categories is iterable:

{Array.isArray(categories) && categories.map((category, index) => (..)}
Heri Hehe Setiawan
  • 1,535
  • 1
  • 12
  • 19
2

async in useEffect will cause a warning, so use it like: Ref

useEffect(()=>{

const fetchData = async () => {
      const a = await getCategories();
      setCategories(a);
    };
 
    fetchData();

},[])

and declare a default empty array in state:

const [categories, setCategories] = useState([]);

Or you can validate that categories are available to iterate like this:

<Nav variant="tabs" >
  {categories.length > 0 && categories.map((category, index) => (
    <Nav.Item key={index}>
      <Nav.Link
      eventKey={`link-${index}`}
      onClick={() => setCategory(category.category)}
      >
        {category}
      </Nav.Link>
    </Nav.Item>
  ))}
</Nav>
bakar_dev
  • 936
  • 4
  • 13
2

Yes, you are right, before rendering, the categories is empty. You have to check whether categories is empty or not before rendering:

return !categories.length ?   
 <Nav variant="tabs" >
   {categories.map((category, index) => (
     <Nav.Item key={index}>
       <Nav.Link
         eventKey={`link-${index}`}
         onClick={() => 
 setCategory(category.category)}
      >
        {category}
      </Nav.Link>
    </Nav.Item>
  ))}
</Nav>

:
<h1>Loading...</h1>
Yudan Zhai
  • 21
  • 3
0

It's difficult to answer without looking at setCategories()

You can solve this with checking if the array exists before you map it

array & array.map(item => {
    return item * 2
})

This won't return undefined because the first condition will fail.

theabrar
  • 360
  • 1
  • 6
  • 15