I’m building a React application where I want to map over an array of items and render them as a list. The issue I’m facing is when the state is initially undefined, the map() function throws the following error:
Uncaught TypeError: Cannot read property 'map' of undefined
Here is the simplified version of my code:
import React, { useState, useEffect } from 'react';
function ItemList() {
const [items, setItems] = useState(); // State initially undefined
useEffect(() => {
// Simulating an API call
setTimeout(() => {
setItems(['Item 1', 'Item 2', 'Item 3']);
}, 2000);
}, []);
return (
<div>
<h1>Item List</h1>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default ItemList;
What I Tried
- I know the error occurs because the state items is initially undefined, and map() cannot be called on undefined.
- I tried adding a conditional check (if (items)), but it still causes issues when items is undefined for a short period after the initial render.
- I also tried initializing the state with an empty array (useState([])), but I would like to understand the best practice for handling such cases where the state might be undefined for a brief moment.
Question
What is the best way to avoid this error while ensuring that the component works smoothly even when the data is being fetched asynchronously? Should I use a default value for the state or apply another method to handle this case efficiently?
2
Answers
Add conditional rendering in case items are not available. you can follow this practice
this is a bit tricky to answer because, in theory, your question is not just a question about technical implementation, but also a consideration about how to approach fetching states in UI.
In short, you could just add a ? to
items?.map()...
This will make it so that the map call will only be executed if the items are not undefined.I still would give you some further suggestions, on which you can chew:
Components in react should be as pure as possible (this is called: component-driven development).
So the first step would be to make "items" a prop of the component ItemList. This ensures we can handle the UI and Service layer in separate function calls, being able to maintain the logic for each in a more predictable manner.
One could argue the concern of an ItemsList can be both the rendering of the list itself and also a state indicating that the Items are currently being fetched, in which case you would display some sort of skeleton / loading spinner, whatever you like.
You could also split these two components in: ItemsList and ItemsListLoading (or something along those lines). The decision can be made depending on the complexity of the loading state, which might actually be much more complicated in some cases.
In my opinion its best to keep data-based props always to a "defined" state and assuming the components will only render with a "defined" data set.
And then in some sort of DataContainer you could have something like this:
Again this is just a solution, Note that I put the logic for fetching in a hook. That is a best practice in react, try to compute and retrieve state in as much functional ways as possible, i.e. writing functions that have a clear in and output and expose the necessary API from the return of the given function.
Hope that helps you a bit to get some ideas how to approach these kinds of problems 🙂