Domov
» Osnovno znanje
»
Kako popraviti napako “Cannot Read Properties of Undefined (Reading map)” v Reactu
Kako popraviti napako “Cannot Read Properties of Undefined (Reading map)” v Reactu
Napaka Reacta Cannot read properties of undefined (reading 'map') pomeni, da je JavaScript poskušal ovrednotiti nekaj, kot je items.map(...), medtem ko je bila vrednost itemsundefined. Pomemben del ni beseda map; pomembna je vrednost neposredno pred .map().
Trenutna dokumentacija Reacta (React 19.2 ob času pisanja) še vedno uporablja metodo JavaScriptovega polja map() kot standardni način pretvorbe zbirk v elemente JSX. MDN definira Array.prototype.map() kot metodo, ki ustvari novo polje z uporabo povratnega klica na vsakem elementu. Če vrednost, za katero pričakujete, da bo polje, ni bila inicializirana, se še ni naložila ali prihaja iz nepričakovane oblike odgovora API, lahko klic odpove, preden React lahko upodobi seznam. Glejte Vodnik Reacta za upodabljanje seznamov in Referenco MDN za Array.prototype.map().
Ilustrativni scenarij: seznam izdelkov, ki sesede, preden se API konča
To je hipotetičen primer za razlago, ne resničen rezultat testa. Predstavljajte si majhno komponento trgovine, ki pridobi izdelke, ko se komponenta prvič pojavi. Razvijalec napiše:
Pri prvem upodabljanju je productsundefined, ker useState ni bilo podane začetne vrednosti. Dokumentacija useState v Reactu navaja, da se vrednost stanja pri prvem upodabljanju ujema z začetnim stanjem, ki ga zagotovite. Pridobivanje podatkov (fetch) teče kasneje, zato lahko upodabljanje doseže products.map(...), preden prispe odgovor. Referenca useState v Reactu.
Ilustracija, ustvarjena z umetno inteligenco, ki kaže na napako JavaScript, ki kaže na upodabljanje seznama, ki kliče .map() na vrednosti undefined.
Korak 1: Natančno poiščite, katera vrednost je undefined
Začnite pri sledi klica (stack trace) in poiščite vrstico, ki vsebuje .map(). V ilustrativni komponenti je ta vrstica products.map(...), zato je products prva vrednost za pregled. V večji komponenti je lahko neuspešen izraz namesto tega data.items.map(), props.users.map() ali response.results.map().
Uporabite razhroščevalnik brskalnika ali začasno beleženje (log) neposredno pred logiko upodabljanja:
To loči več napak, ki so v uporabniškem vmesniku videti podobne. Če je vrednost undefined, preverite inicializacijo ali manjkajočo lastnost. Če je null, morda vaš model nalaganja/podatkov izrecno uporablja null. Če je objekt, morda berete napačno raven odgovora API. Če je niz ali število, je pogodba o podatkih navzgor drugačna od tistega, kar pričakuje komponenta.
Ne zamenjajte samodejno vsakega neuspešnega izraza z opcionalnim veriženjem, preden razumete vrednost. To lahko zatre sesutje, hkrati pa pusti napačen tok podatkov na mestu.
Korak 2: Inicializirajte stanje zbirke kot zbirko, če se to ujema z vašim modelom
Za hipotetični seznam izdelkov je prazno polje smiselno začetno stanje, ker se “še ni naloženih izdelkov” lahko varno predstavi kot zbirka z nič elementi:
const [products, setProducts] = useState([]);
Zdaj lahko prvo upodabljanje zažene products.map(...), ker ima prazno polje metodo map. Ko zahtevek kasneje posodobi stanje, React znova upodobi z vrnjenimi izdelki.
Ilustracija, ustvarjena z umetno inteligenco, ki prikazuje inicializacijo stanja seznama z useState([]), da ima prvo upodabljanje na voljo polje.
To je močna rešitev, ko je stanje konceptualno vedno polje. Manj je primerno, ko undefined ali null nosita pomenljive informacije, kot je “še ni zahtevano”, medtem ko [] pomeni “zahteva je bila končana in ni rezultatov”. V tem primeru ohranite stanja ločena in izrecno upodobite pogoje nalaganja, napake in praznega stanja.
Korak 3: Preverite obliko odgovora API, preden ga shranite v stanje
Inicializacija popravi le prvo upodabljanje. Komponente ne bo zaščitila, če strežnik kasneje vrne drugačno strukturo. Recimo, da API dejansko vrne:
Potem setProducts(data) shrani objekt, ne polja. Verjetna napaka se spremeni v nekaj takega kot products.map is not a function. Pravilna dodelitev bi bila setProducts(data.products), pod pogojem, da je ta lastnost zagotovljeno polje.
Za podatke, ki prečkajo zunanjo mejo, jih validirajte:
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);
});
Ilustracija, ustvarjena z umetno inteligenco, ki prikazuje zaščito upodabljanja seznama z Array.isArray() pred uporabo .map().
To je pomembno, ker “varen privzeti” ne bi smel spremeniti napačnih produkcijskih podatkov v tiho prazno stran. Če pogodba zahteva polje, je beleženje ali prikaz napake lahko koristnejši kot pretvarjanje vsakega nepričakovanega odgovora v [].
Korak 4: Izberite pravo zaščito pri upodabljanju za podatke, ki lahko upravičeno manjkajo
React podpira običajno pogojno upodabljanje JavaScripta. Uradna dokumentacija prikazuje uporabo if, ternarnih izrazov in && za odločanje, kateri JSX naj se vrne. Vodnik Reacta za pogojno upodabljanje.
Za hipotetično stran izdelkov je pogosto najjasnejši izrecen tok nalaganja in napake:
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>
);
Opcionalno veriženje je tudi veljavno, ko “za zdaj manjka” preprosto ne bi smelo proizvesti nobenega preslikanega rezultata:
MDN pojasnjuje, da opcionalno veriženje (?.) prekine izvajanje, ko je vrednost na njegovi levi strani null ali undefined, namesto da bi sprožilo TypeError. Referenca MDN za opcionalno veriženje.
Ilustracija, ustvarjena z umetno inteligenco, ki prikazuje opcionalno veriženje z users?.map(...) za podatke, ki so lahko začasno undefined.
Omejitev je pomembna: products?.map(...) prepreči to specifično napako dostopa do nullish vrednosti, vendar ne dokazuje, da je products polje. Če products postane objekt, se products?.map še vedno razreši na undefined lastnost in poskus klicanja lahko odpove. Uporabite validacijo sheme ali Array.isArray(), ko je vrsta podatkov sama negotova.
Katero popravilo izbrati?
Situacija
Najboljši prvi korak
Zakaj
Stanje bi moralo biti vedno seznam
useState([])
Prvemu upodabljanju zagotovi pravilno vrsto podatkov.
Manjkajoči podatki imajo pomenljivo stanje nalaganja
Pogojno upodabljanje
Ohranja “ni naloženo” ločeno od “naloženo, a prazno”.
Oblika API se lahko razlikuje ali je napačna
Validirajte z Array.isArray() ali shemo
Prepreči, da bi slabi zunanji podatki neopazno vstopili v stanje komponente.
Prop je po zasnovi opcionalen
Opcionalno veriženje ali privzeta vrednost propa
Izogiba se razreševanju legitimno odsotne vrednosti.
Napaka se pojavi po refaktoriranju
Preverite imena lastnosti in propov
Preimenovano polje lahko nekdanje veljavno polje spremeni v undefined.
Pogosti primeri, ki so videti kot ista napaka
API vrne { items: [...] }, komponenta pa pričakuje polje
Preverite odgovor v zavihku Network in dodelite lastnost polja namesto ovojnega objekta. Ne ugibajte oblike iz starega primera ali vmesnika TypeScript, če se živ odgovor razlikuje.
Pri response.data.items.map(...) lahko odpove katera koli manjkajoča vmesna lastnost. Opcionalno veriženje, kot je response?.data?.items, je uporabno za branje negotovih gnezdenih vrednosti, vendar validirajte končno zbirko, preden jo obravnavate kot polje.
Prvo upodabljanje se zgodi, preden se Effect za pridobivanje podatkov zaključi
Effecti v Reactu tečejo po upodabljanju. Dokumentacija Reacta tudi omenja, da je ročno pridobivanje podatkov znotraj Effectov pogosto v odjemalskih aplikacijah, vendar ima lahko slabosti, kot so slapovi (waterfalls), manjkajoči podatki strežniškega upodabljanja in ročno delo s predpomnjenjem/pogojnimi tekmami. Če uporabljate okvir React, je lahko njegov vgrajeni mehanizem za nalaganje podatkov boljša arhitekturna izbira. Referenca useEffect v Reactu.
Varno končna različica ilustrativne komponente
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>
);
}
Ta različica namerno razlikuje med “ni naloženo”, “spodletelo”, “prazno” in “ima podatke”. To je bolj besednato kot en sam izraz z opcionalnim veriženjem, vendar uporabniku zagotovi smiseln uporabniški vmesnik za vsako stanje in omogoči vidnost napačnega odgovora API med razhroščevanjem.
Hiter kontrolni seznam, preden štejetе, da je napaka popravljena
Identificirajte natančno vrednost neposredno pred .map().
Potrdite njeno vrednost in vrsto pri upodabljanju, ki odpove.
Inicializirajte stanje seznama z [], ko prazno polje natančno predstavlja začetno stanje.
Preverite, ali API vrne polje neposredno ali ga ovije v lastnost objekta.
Validirajte zunanje podatke, preden jih shranite v stanje, ko je pogodba pomembna.
Uporabite stanje nalaganja, če undefined pomeni “še ni naloženo”.
Uporabite opcionalno veriženje, ko je odsotnost legitimna, ne kot nadomestilo za razumevanje slabih podatkov.
Po refaktoriranju preverite preimenovane ali izpuščene prope.
Dajte upodobljenim elementom seznama stabilne ključe iz podatkov, kot priporoča dokumentacija Reacta za upodabljanje seznamov.
Za hipotetični seznam izdelkov je bil koren vzroka to, da je prvo upodabljanje prejelo undefined, kjer je komponenta takoj pričakovala polje. V resnični aplikaciji lahko isto sporočilo napake povzroči inicializacija stanja, prope, gnezdena lastnost ali podatki API. Najprej popravite pogodbo o podatkih, nato izberite zaščito pri upodabljanju, ki natančno predstavlja, kaj “manjka” pomeni v vašem uporabniškem vmesniku.