Are you currently experiencing the “Cannot read property ‘map’ of undefined” error in your React app?
This error happens when you call the map()
method on a variable that was meant to be an array, but is actually undefined
. The variable could be a property or state variable.
For example:
import { useState, useEffect } from 'react';
import { fetchBooks } from './books';
function App() {
// ❌ `books` is `undefined`
const [books, setBooks] = useState(undefined);
useEffect(() => {
(async () => {
setBooks(await fetchBooks());
})();
}, []);
return (
<div>
{books.map((book) => (
<div key={book.id}>
<h2>Title: {book.title}</h2>
<p>Author: {book.author}</p>
</div>
))}
</div>
);
}
export default App;
We’ll use this example to learn some easy ways to quickly fix this error in React.
Fix: Use optional chaining operator (?.
)
To fix the “Cannot use property ‘map’ of undefined” error in React.js, use the optional chaining operator (?.
) to access the map()
method:
import { useState, useEffect } from 'react';
import { fetchBooks } from './books';
function App() {
const [books, setBooks] = useState(undefined);
useEffect(() => {
(async () => {
setBooks(await fetchBooks());
})();
}, []);
return (
<div>
{/* ✅ Fix with optional chaining (?.) */}
{books?.map((book) => (
<div key={book.id}>
<h2>Title: {book.title}</h2>
<p>Author: {book.author}</p>
</div>
))}
</div>
);
}
export default App;
The optional chaining operator lets us safely access a property on a value that may be undefined
or null
. If it is, the operator will return undefined
immediately and prevent the property access or method call.
const auth = undefined;
console.log(auth); // undefined
// ✅ No error
console.log(auth?.user?.name); // undefined
Fix: Use AND operator (&&
)
You can also use the &&
operator to check if the value is undefined
before access it in the JSX:
import { useState, useEffect } from 'react';
import { fetchBooks } from './books';
function App() {
const [books, setBooks] = useState(undefined);
useEffect(() => {
(async () => {
setBooks(await fetchBooks());
})();
}, []);
return (
<div>
{/* ✅ Fix with AND operator (&&) */}
{books &&
books.map((book) => (
<div key={book.id}>
<h2>Title: {book.title}</h2>
<p>Author: {book.author}</p>
</div>
))}
</div>
);
}
export default App;
Here’s what a chain of &&
does: it returns the right-most variable if all the variables are truthy. Otherwise, it returns the left-most falsy variable:
console.log(undefined && 0 && []); // undefined
console.log(10 && null && 'coding'); // null
console.log(10 && [5, 7] && 'coding'); // coding
Note: These are the falsy values in JavaScript: undefined
, null
, 0
, false
, ''
(empty string), and NaN
. Every other value is truthy.
Fix: Initialize state variable to empty array
The applies when the array value is a state variable like in our example.
We set the state to an empty array ([]
) at the point where it’s created, so it never becomes undefined
, preventing the “Cannot read property ‘map’ of undefined” error.
import { useState, useEffect } from 'react';
import { fetchBooks } from './books';
function App() {
// ✅ `books` starts out as an array, not `undefined`
const [books, setBooks] = useState([]);
useEffect(() => {
(async () => {
setBooks(await fetchBooks());
})();
}, []);
return (
<div>
{books.map((book) => (
<div key={book.id}>
<h2>Title: {book.title}</h2>
<p>Author: {book.author}</p>
</div>
))}
</div>
);
}
export default App;
Fix: Create a loading state when fetching data
Apart from initializing the state to an empty array you can also display a loading indicator when performing network request, or some other time-consuming operation:
import { useState, useEffect } from 'react';
import { fetchBooks } from './books';
function App() {
const [books, setBooks] = useState(undefined);
const [loading, setLoading] = useState(true);
useEffect(() => {
(async () => {
setBooks(await fetchBooks());
})();
// ✅ Lists books with .map() after loading completes
setLoading(false);
}, []);
let listEl;
if (loading) {
listEl = <div>Loading...</div>;
} else {
listEl = (
<div>
{books.map((book) => (
<div key={book.id}>
<h2>Title: {book.title}</h2>
<p>Author: {book.author}</p>
</div>
))}
</div>
);
}
return <div>{listEl}</div>
}
export default App;
Most web user interfaces have at least three states – loading, error, and success state, and we could be better off representing all three of them:
import { useState, useEffect } from 'react';
import { fetchBooks } from './books';
// TypeScript
// type UIState = 'loading' | 'error' | 'success';
function App() {
const [books, setBooks] = useState(undefined);
const [state, setState] = useState('loading');
// TypeScript
// const [state, setState] = useState<UIState>('loading');
useEffect(() => {
try {
(async () => {
setBooks(await fetchBooks());
setState('success');
})();
// ✅ Lists books with .map() after loading completes
} catch (err) {
setState('error');
}
}, []);
const listViewMap = {
loading: <div>Loading...</div>,
error: <div>An error occured, try again later.</div>,
success: (
<div>
{books.map((book) => (
<div key={book.id}>
<h2>Title: {book.title}</h2>
<p>Author: {book.author}</p>
</div>
))}
</div>
),
};
const listEl = listViewMap[state];
return <div>{listEl}</div>;
}
export default App;
Fix: Check the value assigned to the array
Maybe you initialized the array properly and did everything else right, but you set it to undefined
at some point in the code.
If the array is to be filled with API data, are you sure you’re making the network request properly.
For example, if our fetchBooks()
function was defined like below, it will cause a “Cannot read property ‘map’ of undefined” error when there’s a network error on the user’s device.
export async function fetchBooks() {
const res = fetch('https://api.example/books', {
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
});
// ❌ Will return `undefined` if status code is not 200
return res.status === 200 ? (await res).json() : undefined;
}
Make sure you handle all the edge cases in your app.
Fix: Check your variable names
Sometimes the error happens due to a error in your variable names.
import { useState, useEffect } from 'react';
import { fetchBooks } from './books';
function App() {
const [books, setBooks] = useState([]);
useEffect(() => {
(async () => {
setBooks(await fetchBooks());
})();
}, []);
return (
<div>
{books.map((books) => (
<div key={book.id}>
<h2>Title: {book.title}</h2>
<p>Author: {book.author}</p>
</div>
))}
</div>
);
}
export default App;
Here we mistakenly create a books
variable in the map()
callback but we use book
identifier in the JSX, which is what we actually intended. The causes the unfortunate error, aside from the fact that it shadows the outer books
state variable.