Fix "Cannot read property 'map' of undefined" in React

Last updated on December 07, 2023
Fix "Cannot read property 'map' of undefined" in React

Are you currently experiencing the "Cannot read property 'map' of undefined" error in your React app?

The "Cannot read properties of undefined (reading 'map')" error in the browser console.
The "Cannot read properties of undefined (reading 'map')" error in the browser console.

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.

Coding Beauty Assistant logo

Try Coding Beauty AI Assistant for VS Code

Meet the new intelligent assistant: tailored to optimize your work efficiency with lightning-fast code completions, intuitive AI chat + web search, reliable human expert help, and more.

See also