Početna
» Osnovno znanje
»
Kako riješiti grešku “Cannot Read Properties of Undefined (Reading map)” u Reactu
Kako riješiti grešku “Cannot Read Properties of Undefined (Reading map)” u Reactu
Reactova greška Cannot read properties of undefined (reading 'map') znači da je JavaScript pokušao procijeniti nešto poput items.map(...) dok je items bilo undefined. Važan dio nije riječ map; to je vrijednost koja se nalazi neposredno prije .map().
Trenutna React dokumentacija (React 19.2 u vrijeme pisanja) i dalje koristi JavaScriptovu metodu polja map() kao standardni način pretvaranja kolekcija u JSX elemente. MDN definira Array.prototype.map() kao metodu koja stvara novi niz primjenom povratnog poziva na svaki element. Ako vrijednost koju očekujete da bude niz nije inicijalizirana, još nije učitana ili dolazi iz neočekivanog oblika API odgovora, poziv može ne uspijeti prije nego što React može prikazati popis. Pogledajte Reactov vodič za prikazivanje popisa i MDN referencu za Array.prototype.map().
Ilustrativni scenarij: popis proizvoda koji se ruši prije nego što API završi
Ovo je hipotetski primjer za objašnjenje, a ne stvarni rezultat testa. Zamislite malu komponentu trgovine koja dohvaća proizvode nakon što se komponenta prvi put pojavi. Programer piše:
Prilikom prvog prikaza, products je undefined jer početna vrijednost nije proslijeđena u useState. Dokumentacija za Reactov useState navodi da vrijednost stanja pri prvom prikazu odgovara početnom stanju koje navedete. Dohvaćanje se izvršava naknadno, pa prikaz može doći do products.map(...) prije nego što odgovor stigne. React useState referenca.
Ilustracija generirana AI-jem koja prikazuje JavaScript grešku koja ukazuje na prikaz popisa koji poziva .map() na vrijednosti undefined.
Korak 1: Pronađite točno koja je vrijednost undefined
Počnite od tragova poziva (stack trace) i pronađite redak koji sadrži .map(). U ilustrativnoj komponenti, taj redak je products.map(...), pa je products prva vrijednost koju treba pregledati. U većoj komponenti, neispravni izraz mogao bi biti data.items.map(), props.users.map() ili response.results.map().
Koristite debugger preglednika ili privremeni zapis neposredno prije logike prikaza:
Ovo razdvaja nekoliko grešaka koje u korisničkom sučelju izgledaju slično. Ako je vrijednost undefined, istražite inicijalizaciju ili nedostajuće svojstvo. Ako je null, vaš model učitavanja/podataka možda eksplicitno koristi null. Ako je objekt, možda čitate pogrešnu razinu API odgovora. Ako je niz znakova ili broj, ugovor o podacima gore u nizu razlikuje se od onoga što komponenta očekuje.
Nemojte automatski zamijeniti svaki neispravni izraz opcionalnim povezivanjem (optional chaining) prije nego što razumijete vrijednost. To može prikriti rušenje programa, a da pritom ostavi netočan tok podataka.
Korak 2: Inicijalizirajte stanje kolekcije kao kolekciju kada to odgovara vašem modelu
Za hipotetski popis proizvoda, prazan niz je razumna početna vrijednost jer se “još nema učitanih proizvoda” može sigurno predstaviti kao kolekcija s nula stavki:
const [products, setProducts] = useState([]);
Sada prvi prikaz može izvršiti products.map(...) jer prazan niz ima metodu map. Kada zahtjev naknadno ažurira stanje, React ponovno prikazuje s vraćenim proizvodima.
Ilustracija generirana AI-jem koja prikazuje inicijalizaciju stanja popisa s useState([]) kako bi prvi prikaz imao niz.
Ovo je snažno rješenje kada je stanje konceptualno uvijek niz. Manje je prikladno kada undefined ili null nose značajne informacije, poput “još nije zatraženo”, dok [] znači “zahtjev je završen i nema rezultata”. U tom slučaju, držite stanja odvojenima i eksplicitno prikažite uvjete učitavanja, greške i praznog stanja.
Korak 3: Provjerite oblik API odgovora prije nego što ga stavite u stanje
Inicijalizacija rješava samo prvi prikaz. Neće zaštititi komponentu ako poslužitelj na kraju vrati drugačiju strukturu. Pretpostavimo da API zapravo vraća:
Tada setProducts(data) sprema objekt, a ne niz. Vjerojatna greška mijenja se u nešto poput products.map is not a function. Ispravna dodjela bila bi setProducts(data.products), pod uvjetom da je to svojstvo zajamčeno da bude niz.
Za podatke koji prelaze vanjsku granicu, validirajte ih:
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 generirana AI-jem koja prikazuje zaštitu prikaza popisa s Array.isArray() prije korištenja .map().
Ovo je važno jer “sigurna zadana vrijednost” ne bi trebala pretvoriti neispravne produkcijske podatke u tiho praznu stranicu. Ako ugovor zahtijeva niz, bilježenje ili prikazivanje greške može biti korisnije od pretvaranja svakog neočekivanog odgovora u [].
Korak 4: Odaberite pravu zaštitu pri prikazu za podatke koji mogu legitimno nedostajati
React podržava normalno JavaScript uvjetno prikazivanje. Službena dokumentacija pokazuje korištenje if, ternarnih izraza i && za odlučivanje koji JSX vratiti. React vodič za uvjetno prikazivanje.
Za hipotetsku stranicu proizvoda, eksplicitan tok učitavanja i greške često je najjasniji:
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 povezivanje također je valjano kada “trenutno nedostaje” jednostavno treba proizvesti nema mapiranog rezultata:
MDN objašnjava da opcionalno povezivanje (?.) prekida izvršavanje kada je vrijednost lijevo od njega null ili undefined, umjesto da baca TypeError. MDN referenca za opcionalno povezivanje.
Ilustracija generirana AI-jem koja prikazuje opcionalno povezivanje s users?.map(...) za podatke koji mogu privremeno biti undefined.
Ograničenje je važno: products?.map(...) sprječava ovu specifičnu grešku pristupa nullish vrijednosti, ali ne dokazuje da je products niz. Ako products postane objekt, products?.map i dalje se rješava na undefined svojstvo i pokušaj poziva može ne uspijeti. Koristite validaciju sheme ili Array.isArray() kada je sama vrsta podataka nesigurna.
Koje rješenje biste trebali odabrati?
Situacija
Najbolji prvi potez
Zašto
Stanje bi uvijek trebalo biti popis
useState([])
Daje prvom prikazu ispravnu vrstu podataka.
Nedostajući podaci imaju značajno stanje učitavanja
Uvjetno prikazivanje
Drži “nije učitano” odvojenim od “učitano, ali prazno”.
Oblik API-ja može varirati ili biti neispravan
Validirajte s Array.isArray() ili shemom
Sprječava da loši vanjski podaci neprimijećeni uđu u stanje komponente.
Prop je opcionalan po dizajnu
Opcionalno povezivanje ili zadana vrijednost propa
Preimenovano polje može učiniti da nekada valjan niz postane undefined.
Uobičajeni slučajevi koji izgledaju kao ista greška
API vraća { items: [...] }, ali komponenta očekuje niz
Pregledajte odgovor mreže i dodijelite svojstvo niza, a ne objekt omotač. Nemojte nagađati oblik iz starog primjera ili TypeScript sučelja ako se stvarni odgovor razlikuje.
S response.data.items.map(...), bilo koje nedostajuće posredno svojstvo može uzrokovati neuspjeh. Opcionalno povezivanje poput response?.data?.items korisno je za čitanje nesigurnih ugniježđenih vrijednosti, ali validirajte konačnu kolekciju prije nego što je tretirate kao niz.
Prvi prikaz događa se prije nego što Effect dohvaćanja završi
React Effects izvršavaju se nakon prikaza. Reactova dokumentacija također napominje da je ručno dohvaćanje podataka unutar Effects uobičajeno u klijentskim aplikacijama, ali može imati nedostatke poput kaskadnih zahtjeva (waterfalls), nedostajućih podataka server-side renderiranja i ručnog rada s predmemorijom/uvjetima utrkivanja. Ako koristite React okvir, njegova ugrađena mehanizam za učitavanje podataka može biti bolji arhitektonski izbor. React useEffect referenca.
Sigurnija konačna verzija 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>
);
}
Ova verzija namjerno razlikuje “nije učitano”, “neuspjelo”, “prazno” i “ima podataka”. To je opširnije od jednog izraza opcionalnog povezivanja, ali korisniku daje značajno korisničko sučelje za svako stanje i čini neispravan API odgovor vidljivim tijekom otklanjanja grešaka.
Brzi popis provjera prije nego što smatrate da je greška riješena
Identificirajte točnu vrijednost neposredno prije .map().
Potvrdite njezinu vrijednost i vrstu pri prikazu koji ne uspijeva.
Inicijalizirajte stanje popisa s [] kada prazan niz točno predstavlja početno stanje.
Provjerite vraća li API niz izravno ili ga omata u svojstvo objekta.
Validirajte vanjske podatke prije spremanja u stanje kada je ugovor važan.
Koristite stanje učitavanja ako undefined znači “još nije učitano”.
Koristite opcionalno povezivanje kada je odsutnost legitimna, a ne kao zamjenu za razumijevanje loših podataka.
Provjerite preimenovane ili izostavljene propove nakon refaktoriranja.
Dajte prikazanim stavkama popisa stabilne ključeve iz podataka, kako preporučuje Reactova dokumentacija za prikazivanje popisa.
Za hipotetski popis proizvoda, uzrok je bio prvi prikaz koji je primio undefined tamo gdje je komponenta odmah očekivala niz. U stvarnoj aplikaciji, ista poruka o grešci može potjecati od inicijalizacije stanja, propova, ugniježđenih svojstava ili API podataka. Prvo popravite ugovor o podacima, a zatim odaberite zaštitu pri prikazu koja točno predstavlja što “nedostaje” znači u vašem korisničkom sučelju.