Kezdőlap
» Alap tudás
»
Hogyan javítsd a „Cannot Read Properties of Undefined (Reading map)” hibát Reactben
Hogyan javítsd a „Cannot Read Properties of Undefined (Reading map)” hibát Reactben
A React Cannot read properties of undefined (reading 'map') hibaüzenete azt jelenti, hogy a JavaScript megpróbált kiértékelni egy olyan kifejezést, mint az items.map(...), miközben az items értéke undefined volt. A lényeg nem a map szó, hanem az a érték, amely közvetlenül a .map() előtt áll.
Az aktuális React dokumentáció (írásunk pillanatában a React 19.2) továbbra is a JavaScript tömb map() módszerét használja a gyűjtemények JSX elemekké alakításának szabványos módjaként. Az MDN az Array.prototype.map()-ot úgy definiálja, mint egy módszert, amely új tömböt hoz létre egy callback függvény minden elemre történő alkalmazásával. Ha az az érték, amelyről azt várjuk, hogy tömb legyen, nincs inicializálva, még nem töltődött be, vagy egy váratlan API-válaszból származik, a hívás sikertelen lehet, mielőtt a React renderelhetné a listát. Lásd a React útmutatóját a listák rendereléséhez és az MDN Array.prototype.map() referenciáját.
Illusztratív forgatókönyv: egy terméklista, amely összeomlik, mielőtt az API befejezné
Ez egy hipotetikus példa a magyarázat céljából, nem valós tesztelési eredmény. Képzeljünk el egy kis áruház komponenst, amely a komponens megjelenése után tölti be a termékeket. A fejlesztő a következő kódot írja:
Az első rendereléskor a products értéke undefined, mert nem adtunk át kezdeti értéket a useState-nek. A React useState dokumentációja kimondja, hogy az állapot értéke az első rendereléskor megegyezik a megadott kezdeti állapottal. A fetch futás ezután következik, így a renderelés elérheti a products.map(...) részt, mielőtt a válasz megérkezik. React useState referencia.
AI-generált illusztráció a JavaScript hibáról, amely egy olyan lista renderelésére mutat, amely .map()-ot hív egy undefined értéken.
1. lépés: Találd meg pontosan, melyik érték undefined
Kezdd a stack trace-nél, és keresd meg azt a sort, amely tartalmazza a .map()-ot. Az illusztratív komponensben ez a sor a products.map(...), tehát a products az első ellenőrizendő érték. Egy nagyobb komponensben a hibás kifejezés lehet data.items.map(), props.users.map() vagy response.results.map() is.
Használd a böngésző hibakeresőjét vagy egy ideiglenes naplózási utasítást közvetlenül a renderelési logika előtt:
Ez szétválaszt több, a felhasználói felületen hasonlóan kinéző hibát. Ha az érték undefined, vizsgálja meg az inicializálást vagy egy hiányzó tulajdonságot. Ha null, a betöltési/adatmodellje explicit módon használhat nullt. Ha objektum, akkor lehet, hogy az API-válasz rossz szintjét olvassa. Ha string vagy szám, a felsőbb réteg adatkontraktuma eltér attól, amit a komponens vár.
Ne automatikusan cseréld le minden hibás kifejezést opcionális láncolásra, mielőtt megértenéd az értéket. Ez elfojthatja az összeomlást, miközben a hibás adatfolyam a helyén marad.
2. lépés: Inicializáld a gyűjtemény állapotát gyűjteményként, ha ez megfelel a modellnek
A hipotetikus terméklista esetén egy üres tömb ésszerű kezdeti állapot, mert a „még nem töltődtek be termékek” biztonságosan reprezentálható nulla elemű gyűjteményként:
const [products, setProducts] = useState([]);
Most az első renderelés futtathatja a products.map(...)-ot, mert egy üres tömbnek van map módszere. Amikor a kérés később frissíti az állapotot, a React újra renderel a visszakapott termékekkel.
AI-generált illusztráció a lista állapotának inicializálásáról useState([]) segítségével, hogy az első renderelésnek legyen tömbje.
Ez erős megoldás, ha az állapot fogalmilag mindig tömb. Kevésbé megfelelő, ha az undefined vagy null jelentőséggel bír, például „még nem kérték le”, míg a [] azt jelenti, hogy „a kérés befejeződött, és nulla eredmény van”. Ebben az esetben tartsd külön az állapotokat, és jelenítsd meg explicit módon a betöltési, hiba és üres feltételeket.
3. lépés: Ellenőrizd az API-válasz struktúráját, mielőtt az állapotba helyeznéd
Az inicializálás csak az első renderelést javítja meg. Nem védi meg a komponenst, ha a szerver végül más struktúrát ad vissza. Tegyük fel, hogy az API valójában ezt adja vissza:
Ekkor a setProducts(data) egy objektumot tárol, nem tömböt. A valószínű hiba valami olyasmivé válik, mint a products.map is not a function. A helyes hozzárendelés a setProducts(data.products) lenne, feltéve, hogy ez a tulajdonság garantáltan tömb.
Külső határt átlépő adatok esetén validáld őket:
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-generált illusztráció a lista renderelésének védelméről Array.isArray() használatával a .map() előtt.
Ez azért fontos, mert egy „biztonságos alapértelmezés” nem alakíthatja át a hibás éles adatokat csendben üres oldallá. Ha a szerződés szerint tömb szükséges, a naplózás vagy a hiba megjelenítése hasznosabb lehet, mint minden váratlan válasz []-ra alakítása.
4. lépés: Válaszd ki a megfelelő renderelési védelmet a jogosan hiányzó adatokhoz
A React támogatja a normál JavaScript feltételes renderelést. A hivatalos dokumentáció azt mutatja, hogyan használhatók az if, a ternáris kifejezések és a && a visszaadandó JSX eldöntésére. React feltételes renderelési útmutató.
A hipotetikus termékoldalon egy explicit betöltési és hibaáram gyakran a legátláthatóbb:
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>
);
Az opcionális láncolás is érvényes, ha a „jelenleg hiányzik” egyszerűen nem eredményez leképezett kimenetet:
Az MDN elmagyarázza, hogy az opcionális láncolás (?.) megszakítja a végrehajtást, ha a bal oldali értéke null vagy undefined, ahelyett, hogy TypeErro-t dobna. MDN opcionális láncolási referencia.
AI-generált illusztráció az opcionális láncolásról users?.map(...) formában ideiglenesen undefined adatok esetén.
A korlát fontos: a products?.map(...) megakadályozza ezt a konkrét nullish-hozzáférési hibát, de nem bizonyítja, hogy a products tömb. Ha a products objektummá válik, a products?.map továbbra is egy undefined tulajdonságra hivatkozik, és a hívása továbbra is sikertelen lehet. Használj sémavalidációt vagy Array.isArray()-t, ha maga az adattípus bizonytalan.
Melyik megoldást válaszd?
Helyzet
Legjobb első lépés
Miért
Az állapotnak mindig listának kell lennie
useState([])
Megadja az első renderelésnek a helyes adattípust.
A hiányzó adatnak jelentős betöltési állapota van
Feltételes renderelés
Külön tartja a „nem betöltött” és a „betöltött, de üres” állapotot.
Az API struktúrája változó vagy hibás lehet
Validálás Array.isArray()-vel vagy sémával
Megakadályozza, hogy a rossz külső adat észrevétlenül kerüljön a komponens állapotába.
A prop tervezés szerint opcionális
Opcionális láncolás vagy alapértelmezett prop érték
Kerüli a jogosan hiányzó érték de-referenciálását.
A hiba refaktorálás után jelenik meg
Ellenőrizd a tulajdonság- és propneveket
Egy átnevezett mező miatt egy egykor érvényes tömb undefineddé válhat.
Gyakori esetek, amelyek ugyanannak a hibának tűnnek
Az API { items: [...] }-t ad vissza, de a komponens tömböt vár
Vizsgáld meg a Network választ, és a wrapper objektum helyett a tömb tulajdonságot rendeld hozzá. Ne találgasd a struktúrát egy régi példából vagy TypeScript interfészből, ha az élő válasz eltér.
Egy szülő komponens elfelejti átadni a propot
Ez fog elbukni:
function UserList({ users }) {
return users.map(user => <div key={user.id}>{user.name}</div>);
}
// A szülő véletlenül kihagyja a users propot:
<UserList />
Ha egy kihagyott propnak üres listát kell jelentenie, egy alapértelmezett érték explicitéssé teszi ezt a szerződést:
A response.data.items.map(...) esetén bármely hiányzó köztes tulajdonság okozhat hibát. Az opcionális láncolás, mint a response?.data?.items, hasznos bizonytalan beágyazott értékek olvasásához, de validáld a végső gyűjteményt, mielőtt tömbként kezelnéd.
Az első renderelés megtörténik, mielőtt a fetch Effect befejeződik
A React Effects a renderelés után futnak. A React dokumentációja azt is megjegyzi, hogy az adatok manuális lekérése az Effects-en belül gyakori kliensoldali alkalmazásokban, de hátrányai lehetnek, mint például a vízesésszerű hívások, a hiányzó szerveroldali renderelt adatok, valamint a manuális gyorsítótárazási/versenyfeltétel-munka. Ha React keretrendszert használsz, annak beépített adatbetöltési mechanizmusa jobb architekturális választás lehet. React useEffect referencia.
Az illusztratív komponens biztonságosabb végső verziója
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>
);
}
Ez a verzió szándékosan különbséget tesz a „nem betöltött”, „sikertelen”, „üres” és „van adat” állapotok között. Ez bőbeszédűbb, mint egyetlen opcionális láncolási kifejezés, de a felhasználónak minden állapothoz jelentőségteljes UI-t biztosít, és debugolás közben láthatóvá teszi a hibás API-válaszokat.
Gyors ellenőrzőlista, mielőtt a hibát javítottnak tekinted
Azonosítsd a .map() előtt álló pontos értéket.
Erősítsd meg az értékét és típusát a hibás renderelésnél.
Inicializáld a lista állapotát []-vel, ha az üres tömb pontosan reprezentálja a kezdeti állapotot.
Ellenőrizd, hogy az API közvetlenül tömböt ad-e vissza, vagy egy objektum tulajdonságba csomagolja.
Validáld a külső adatokat, mielőtt az állapotba mentenéd őket, ha a szerződés fontos.
Használj betöltési állapotot, ha az undefined azt jelenti, hogy „még nem töltődött be”.
Használj opcionális láncolást, ha a hiány jogos, ne pedig a rossz adatok megértésének helyettesítőjeként.
Ellenőrizd az átnevezett vagy kihagyott propokat refaktorálások után.
Adj a renderelt listaelemeknek stabil kulcsokat az adatokból, ahogy a React listarendezési dokumentációja javasolja.
A hipotetikus terméklista esetén a gyökérok az volt, hogy az első renderelés undefined-t kapott ott, ahol a komponens azonnal tömböt várt. Egy valós alkalmazásban ugyanaz a hibaüzenet származhat állapotinicializálásból, propokból, beágyazott tulajdonságokból vagy API-adatokból. Először javítsd az adatkontraktumot, majd válassz egy renderelési védelmet, amely pontosan reprezentálja, mit jelent a „hiányzik” a felhasználói felületeden.