Как да поправите грешката „Cannot read properties of undefined (reading 'map')“ в React

Грешката в React Cannot read properties of undefined (reading 'map') означава, че JavaScript е опитал да оцени нещо като items.map(...), докато items е било undefined. Важната част не е думата map; това е стойността непосредствено преди .map().

Актуалната документация на React (React 19.2 към момента на писане) все още използва метода map() на масивите в JavaScript като стандартен начин за превръщане на колекции в JSX елементи. MDN дефинира Array.prototype.map() като метод, който създава нов масив, като прилага обратна извикана функция (callback) към всеки елемент. Ако стойността, която очаквате да бъде масив, не е инициализирана, все още не е заредена или идва от неочаквана структура на API, извикването може да се провали, преди React да успее да рендира списъка. Вижте ръководството на React за рендиране на списъци и референцията на MDN за Array.prototype.map().

Илюстративен сценарий: списък с продукти, който крашва, преди API да приключи

Това е хипотетичен пример за обяснение, а не реален резултат от тест. Представете си малък компонент за витрина, който извлича продукти след първото появяване на компонента. Разработчикът пише:

function ProductList() {
  const [products, setProducts] = useState();

  useEffect(() => {
    fetch('/api/products')
      .then(response => response.json())
      .then(data => setProducts(data));
  }, []);

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}

При първото рендиране products е undefined, защото не е подадена начална стойност на useState. Документацията на React за useState посочва, че стойността на състоянието при първото рендиране съвпада с предоставеното начално състояние. Извличането (fetch) се изпълнява след това, така че рендирането може да достигне до products.map(...), преди отговорът да пристигне. Референция на React useState.

Илюстрация, генерирана от AI, показваща конзолата на браузъра с грешката Cannot read properties of undefined reading map в React компонент.
Илюстрация, генерирана от AI, на JavaScript грешката, сочеща към рендиране на списък, което извиква .map() върху undefined стойност.

Стъпка 1: Намерете точно коя стойност е undefined

Започнете от стековата следа (stack trace) иLocирайте реда, съдържащ .map(). В илюстративния компонент този ред е products.map(...), така че products е първата стойност за проверка. В по-голям компонент проваленият израз може да бъде вместо това data.items.map(), props.users.map() или response.results.map().

Използвайте дебъгера на браузъра или временен лог непосредствено преди логиката за рендиране:

console.log('products:', products);
console.log('is array:', Array.isArray(products));

Това разграничава няколко бъга, които изглеждат подобно в UI. Ако стойността е undefined, разследвайте инициализацията или липсващо свойство. Ако е null, вашият модел за зареждане/данни може изрично да използва null. Ако е обект, може да четете грешното ниво на отговор от API. Ако е низ или число, горният поток на данните (upstream data contract) е различен от очаквания от компонента.

Не заменяйте автоматично всеки провалещ се израз с опционално верижване (optional chaining), преди да разберете стойността. Това може да потисне краша, като остави некоректния поток на данни на място.

Стъпка 2: Инициализирайте състоянието на колекцията като колекция, когато това съответства на вашия модел

За хипотетичния списък с продукти, празният масив е разумно начално състояние, защото „няма заредени продукти все още“ може безопасно да бъде представено като колекция с нула елемента:

const [products, setProducts] = useState([]);

Сега първото рендиране може да изпълни products.map(...), защото празният масив има метод map. Когато заявката по-късно актуализира състоянието, React рендира отново с върнатите продукти.

Илюстрация, генерирана от AI, на декларация на React useState, инициализирана с празен масив.
Илюстрация, генерирана от AI, на инициализиране на състоянието на списъка с useState([]), така че първото рендиране да има масив.

Това е силно решение, когато състоянието концептуално винаги е масив. То е по-малко подходящо, когато undefined или null носят смислена информация, като „не е заявено все още“, докато [] означава „заявката приключи и има нула резултата“. В този случай запазете състоянията разграничени и рендирайте изрично условията за зареждане, грешка и празно състояние.

Стъпка 3: Проверете формата на отговора от API, преди да го поставите в състоянието

Инициализацията поправя само първото рендиране. Тя няма да защити компонента, ако сървърът в крайна сметка върне различна структура. Представете си, че API всъщност връща:

{
  "products": [
    { "id": 1, "name": "Keyboard" },
    { "id": 2, "name": "Mouse" }
  ]
}

Тогава setProducts(data) съхранява обект, а не масив. Вероятната грешка се променя на нещо като products.map is not a function. Правилното присвояване би било setProducts(data.products), при условие че това свойство е гарантирано да бъде масив.

За данни, преминаващи през външна граница, ги валидирайте:

fetch('/api/products')
  .then(response => response.json())
  .then(data => {
    if (!Array.isArray(data.products)) {
      throw new Error('Expected data.products to be an array');
    }
    setProducts(data.products);
  })
  .catch(error => {
    console.error(error);
    setError(error);
  });
Илюстрация, генерирана от AI, на React компонент, проверяващ Array.isArray преди извикване на map.
Илюстрация, генерирана от AI, на предпазител за рендиране на списък с Array.isArray() преди използване на .map().

Това е важно, защото „безопасната стойност по подразбиране“ не трябва да превръща некоректните производствени данни в тихо празна страница. Ако масивът е изискван от контракта, логването или показването на грешка може да е по-полезно от превръщането на всеки неочакван отговор в [].

Стъпка 4: Изберете правилния предпазител при рендиране за данни, които може легитимно да липсват

React поддържа нормално условно рендиране в JavaScript. Официалната документация показва използването на if, тернарни изрази и &&, за да се реши кой JSX да се върне. Ръководство за условно рендиране в React.

За хипотетичната страница с продукти, изричен поток за зареждане и грешка често е най-ясният:

if (error) {
  return <p>Could not load products.</p>;
}

if (products === undefined) {
  return <p>Loading products…</p>;
}

if (products.length === 0) {
  return <p>No products found.</p>;
}

return (
  <ul>
    {products.map(product => (
      <li key={product.id}>{product.name}</li>
    ))}
  </ul>
);

Опционалното верижване също е валидно, когато „липсва за момента“ трябва просто да произведе няма мапиран резултат:

{products?.map(product => (
  <ProductCard key={product.id} product={product} />
))}

MDN обяснява, че опционалното верижване (?.) прекъсва изпълнението (short-circuits), когато стойността отляво му е null или undefined, вместо да хвърли TypeError. Референция на MDN за опционално верижване.

Илюстрация, генерирана от AI, на опционално верижване, използвано преди map в React компонент.
Илюстрация, генерирана от AI, на опционално верижване с users?.map(...) за данни, които временно може да са undefined.

Ограничението е важно: products?.map(...) предотвратява тази конкретна грешка при достъп до nullish стойност, но не доказва, че products е масив. Ако products стане обект, products?.map все пак се разрешава до undefined свойство и опитът за извикването му може да се провали. Използвайте валидация на схемата или Array.isArray(), когато самият тип данни е несигурен.

Кое решение трябва да изберете?

СитуацияНай-добър първи ходЗащо
Състоянието винаги трябва да е списъкuseState([])Давa на първото рендиране правилния тип данни.
Липсващите данни имат смислено състояние на зарежданеУсловно рендиранеПази „не е заредено“ отделно от „заредено, но празно“.
Формата на API може да варира или да е некоректнаВалидирайте с Array.isArray() или схемаПредотвратява лоши външни данни да влязат в състоянието на компонента незабелязано.
Пропът е опционален по дизайнОпционално верижване или стойност на проп по подразбиранеИзбягва разпакетирането (dereferencing) на легитимно отсъстваща стойност.
Грешката се появява след рефакторингПроверете имената на свойствата и проповетеПреименувано поле може да направи някога валиден масив да стане undefined.

Чести случаи, които изглеждат като една и съща грешка

API връща { items: [...] }, но компонентът очаква масив

Инспектирайте отговора в мрежовия таб (Network) и присвоете свойството с масива, а не обвиващия обект. Не отгатвайте формата от стар пример или TypeScript интерфейс, ако живият отговор се различава.

Родителският компонент забравя да подаде пропът

Това ще се провали:

function UserList({ users }) {
  return users.map(user => <div key={user.id}>{user.name}</div>);
}

// Parent accidentally omits users:
<UserList />

Ако пропуснат проп трябва да означава празен списък, стойността по подразбиране прави този контракт явен:

function UserList({ users = [] }) {
  return users.map(user => <div key={user.id}>{user.name}</div>);
}

Липсва вложено свойство

При response.data.items.map(...), всяко липсващо междинно свойство може да доведе до провал. Опционалното верижване като response?.data?.items е полезно за четене на несигурни вложени стойности, но валидирайте крайната колекция, преди да я третирате като масив.

Първото рендиране се случва, преди Effect за извличане да приключи

React Effects се изпълняват след рендирането. Документацията на React също отбелязва, че ръчното извличане на данни вътре в Effects е често срещано в клиентски приложения, но може да има недостатъци като водопади (waterfalls), липсващи данни от сървърно рендиране и ръчна работа по кеширането/състоянията на надпревара (race-conditions). Ако използвате React framework, неговият вграден механизъм за зареждане на данни може да е по-добър архитектурен избор. Референция на React useEffect.

По-безопасна фина версия на илюстративния компонент

import { useEffect, useState } from 'react';

export default function ProductList() {
  const [products, setProducts] = useState(undefined);
  const [error, setError] = useState(null);

  useEffect(() => {
    let ignore = false;

    fetch('/api/products')
      .then(response => {
        if (!response.ok) throw new Error('Request failed');
        return response.json();
      })
      .then(data => {
        if (!Array.isArray(data.products)) {
          throw new Error('Expected products array');
        }
        if (!ignore) setProducts(data.products);
      })
      .catch(error => {
        if (!ignore) setError(error);
      });

    return () => {
      ignore = true;
    };
  }, []);

  if (error) return <p>Could not load products.</p>;
  if (products === undefined) return <p>Loading products…</p>;
  if (products.length === 0) return <p>No products found.</p>;

  return (
    <ul>
      {products.map(product => (
        <li key={product.id}>{product.name}</li>
      ))}
    </ul>
  );
}

Тази версия намерено разграничава „не е заредено“, „неуспех“, „празно“ и „има данни“. Това е по-многословно от един израз с опционално верижване, но дава на потребителя смислен UI за всяко състояние и прави некоректния отговор от API видим по време на дебъгване.

Бърз чеклист, преди да считате бъга за поправен

  • Идентифицирайте точната стойност непосредствено преди .map().
  • Потвърдете нейната стойност и тип при рендирането, което се проваля.
  • Инициализирайте състоянието на списъка с [], когато празният масив точно представлява началното състояние.
  • Проверете дали API връща масив директно или го обвива в свойство на обект.
  • Валидирайте външните данни, преди да ги съхраните в състоянието, когато контрактът е важен.
  • Използвайте състояние на зареждане, ако undefined означава „не е заредено все още“.
  • Използвайте опционално верижване, когато отсъствието е легитимно, а не като заместител на разбирането на лошите данни.
  • Проверете преименувани или пропуснати пропове след рефакторинги.
  • Дайте на рендираните елементи от списъка стабилни ключове от данните, както се препоръчва от документацията на React за рендиране на списъци.

За хипотетичния списък с продукти коренната причина беше, че първото рендиране получава undefined, където компонентът веднага очаква масив. В реално приложение същото съобщение за грешка може да идва от инициализацията на състоянието, проповете, вложените свойства или данните от API. Поправете контракта за данните първо, след което изберете предпазител при рендиране, който точно представлява какво означава „липсва“ във вашия UI.

Оставете коментар

Как да поправите грешката Uncaught ReferenceError: process is not defined във Vite

Как да поправите грешката Uncaught ReferenceError: process is not defined във Vite

Поправете грешката "process is not defined" във Vite, като замените използването на process.env в стил Node.js, конфигурирате правилно променливите с префикс VITE_ и проверите зависимостите.

Как да поправите грешката „PyTorch CUDA Out of Memory“ по време на обучение на модел

Как да поправите грешката „PyTorch CUDA Out of Memory“ по време на обучение на модел

Поправете грешките за липса на памет в PyTorch CUDA с практичен работен процес: измерете паметта на GPU, намалете работния набор, използвайте AMP и акумулиране на градиенти, създайте контролни точки на активациите и настройте аллокатора само когато е необходимо.

Как да поправите липсващия CORS заглавен ред Access-Control-Allow-Origin в Express.js

Как да поправите липсващия CORS заглавен ред Access-Control-Allow-Origin в Express.js

Поправете грешката за липсващ Access-Control-Allow-Origin CORS в Express.js, като диагностицирате произхода, конфигурирате cors безопасно, обработвате предварителните заявки (preflight) и проверявате заглавните редове.

Как да поправите грешката „Cannot read properties of undefined (reading 'map')“ в React

Как да поправите грешката „Cannot read properties of undefined (reading 'map')“ в React

Поправете грешката на React „Cannot read properties of undefined (reading 'map')“, като проследите undefined стойността, коригирате състоянието и данните от API и добавите безопасни предпазители при рендиране.

Как да поправите грешката „Module Not Found: Can’t Resolve fs“ в Webpack

Как да поправите грешката „Module Not Found: Can’t Resolve fs“ в Webpack

Поправете грешката на Webpack „Can’t resolve 'fs'“, като изберете правилното решение: преместете кода, предназначен само за Node, от сървърната страна, използвайте зависимост, безопасна за браузър, задайте fs:false само когато е опционално, или задайте правилната цел за Node.

Как да поправите грешката „Supabase API Key Not Found“ в променливите на средата

Как да поправите грешката „Supabase API Key Not Found“ в променливите на средата

Поправете липсващи Supabase API ключове в Next.js, Vite, Node, деплойменти и Edge Functions. Използвайте текущите имена на publishable/secret ключовете, правилните env файлове и безопасни стъпки за проверка.

Как да поправите грешката „Flutter Command Not Found“ в PATH на macOS

Как да поправите грешката „Flutter Command Not Found“ в PATH на macOS

Поправете грешката „flutter: command not found“ на macOS, като локализирате Flutter SDK, добавите папката bin към PATH, презаредите Zsh и проверите настройките.

Как да поправите грешката „Порт 8080 вече се използва“ в терминала на Windows, macOS и Linux

Как да поправите грешката „Порт 8080 вече се използва“ в терминала на Windows, macOS и Linux

Поправете грешката „Порт 8080 вече се използва“, като намерите процеса, който притежава порта, спрете го безопасно, обработете Docker или изберете нов порт.

Как да поправите грешката в Django “ImproperlyConfigured: The SECRET_KEY Setting Must Not Be Empty”

Как да поправите грешката в Django “ImproperlyConfigured: The SECRET_KEY Setting Must Not Be Empty”

Поправете грешката на Django SECRET_KEY must not be empty, като проверите активния модул за настройки, променливите на средата, генерирането на ключ и конфигурацията за продуктивна среда.

Как да поправите грешката „Connection Refused“ на PostgreSQL за localhost порт 5432

Как да поправите грешката „Connection Refused“ на PostgreSQL за localhost порт 5432

Поправете грешката „connection refused“ на localhost:5432, като проверите статуса на сървъра, pg_isready, слушането на порта, postgresql.conf, Docker мапиранията и автентикацията.