Hjem
» Basis viden
»
Sådan løser du “Cannot Read Properties of Undefined (Reading map)” i React
Sådan løser du “Cannot Read Properties of Undefined (Reading map)” i React
React-fejlen Cannot read properties of undefined (reading 'map') betyder, at JavaScript forsøgte at evaluere noget i stil med items.map(...), mens items var undefined. Det vigtige er ikke ordet map; det er værdien umiddelbart før .map().
Nuværende React-dokumentation (React 19.2 på tidspunktet for skrivningen) bruger stadig JavaScripts array-metode map() som den standardmåde at konvertere samlinger til JSX-elementer på. MDN definerer Array.prototype.map() som en metode, der opretter et nyt array ved at anvende en callback på hvert element. Hvis den værdi, du forventer er et array, ikke er initialiseret, ikke er indlæst endnu, eller kommer fra en uventet API-struktur, kan kaldet fejle, før React kan rendere listen. Se Reacts guide til rendering af lister og MDNs reference til Array.prototype.map().
Illustrerende scenarie: en produktliste, der crasher, før API'en er færdig
Dette er et hypotetisk eksempel til forklaring, ikke et reelt testresultat. Forestil dig en lille butikskomponent, der henter produkter, efter at komponenten først er vist. Udvikleren skriver:
Ved den første rendering er productsundefined, fordi der ikke blev sendt en startværdi til useState. Reacts useState-dokumentation angiver, at state-værdien ved den første rendering svarer til den startstate, du angiver. Fetch-kaldet kører bagefter, så renderingen kan nå products.map(...), før svaret ankommer. React useState reference.
AI-genereret illustration af JavaScript-fejlen, der peger på en liste-rendering, der kalder .map() på en udefineret værdi.
Trin 1: Find præcis hvilken værdi der er undefined
Start ved stack-traceet og find linjen, der indeholder .map(). I den illustrerende komponent er det linjen products.map(...), så products er den første værdi at undersøge. I en større komponent kan det fejlede udtryk i stedet være data.items.map(), props.users.map() eller response.results.map().
Brug browserens debugger eller en midlertidig log lige før renderingslogikken:
Dette adskiller flere fejl, der ligner hinanden i UI'et. Hvis værdien er undefined, skal du undersøge initialisering eller en manglende egenskab. Hvis den er null, bruger din indlæsnings-/datamodel måske eksplicit null. Hvis den er et objekt, læser du måske det forkerte niveau i et API-svar. Hvis den er en streng eller et tal, er den opstrøms datakontrakt anderledes, end komponenten forventer.
Erstat ikke automatisk hvert fejlede udtryk med optional chaining, før du forstår værdien. Det kan undertrykke crashet, mens den forkerte dataflow forbliver på plads.
Trin 2: Initialisér collections-state som en collection, når det passer til din model
For den hypotetiske produktliste er et tomt array en fornuftig startstate, fordi “ingen produkter indlæst endnu” sikkert kan repræsenteres som en samling med nul elementer:
const [products, setProducts] = useState([]);
Nu kan den første rendering køre products.map(...), fordi et tomt array har en map-metode. Når anmodningen senere opdaterer staten, renderer React igen med de returnerede produkter.
AI-genereret illustration af initialisering af liste-state med useState([]), så den første rendering har et array.
Dette er en stærk løsning, når staten konceptuelt altid er et array. Det er mindre hensigtsmæssigt, når undefined eller null bærer meningsfuld information, såsom “ikke anmodet om endnu”, mens [] betyder “anmodningen er afsluttet, og der er nul resultater”. I det tilfælde skal du holde statene adskilte og rendere indlæsnings-, fejl- og tomme tilstande eksplicit.
Trin 3: Verificér API-svarets form, før du lægger det i state
Initialisering løser kun den første rendering. Det vil ikke beskytte komponenten, hvis serveren til sidst returnerer en anden struktur. Antag at API'en faktisk returnerer:
Så gemmer setProducts(data) et objekt, ikke et array. Den sandsynlige fejl ændres til noget i stil med products.map is not a function. Den korrekte tildeling ville være setProducts(data.products), forudsat at den egenskab er garanteret at være et array.
For data, der krydser en ekstern grænse, skal du validere den:
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-genereret illustration af at beskytte en liste-rendering med Array.isArray(), før .map() bruges.
Dette er vigtigt, fordi en “sikker standard” ikke bør omdanne fejlbehæftede produktionsdata til en stille tom side. Hvis et array kræves af kontrakten, kan det være mere nyttigt at logge eller vise en fejl end at konvertere hvert uventet svar til [].
Trin 4: Vælg den rigtige renderingsguard for data, der legitimt kan mangle
React understøtter normal JavaScript betinget rendering. De officielle docs viser brug af if, ternære udtryk og && til at bestemme, hvilket JSX der skal returneres. React guide til betinget rendering.
For den hypotetiske produktside er en eksplicit indlæsnings- og fejlflow ofte den tydeligste:
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>
);
Optional chaining er også gyldigt, når “mangler for nu” blot skal producere intet kortlagt resultat:
MDN forklarer, at optional chaining (?.) afbryder (short-circuits), når værdien til venstre for den er null eller undefined, i stedet for at kaste en TypeError. MDN reference til optional chaining.
AI-genereret illustration af optional chaining med users?.map(...) for data, der midlertidigt kan være undefined.
Begrænsningen er vigtig: products?.map(...) forhindrer denne specifikke nullish-access-fejl, men det beviser ikke, at products er et array. Hvis products bliver et objekt, vil products?.map stadig løse til en udefineret egenskab, og forsøget på at kalde den kan fejle. Brug skemavalidering eller Array.isArray(), når datatypen i sig selv er usikker.
Hvilken løsning skal du vælge?
Situation
Bedste første træk
Hvorfor
Staten skal altid være en liste
useState([])
Giver den første rendering den korrekte datatype.
Manglende data har en meningsfuld indlæsningsstate
Betinget rendering
Holder “ikke indlæst” adskilt fra “indlæst men tom”.
API-formen kan variere eller være fejlbehæftet
Validér med Array.isArray() eller et skema
Forhindrer dårlige eksterne data i at komme ind i komponentstaten uden at blive opdaget.
Prop er valgfri som design
Optional chaining eller en standard prop-værdi
Undgår at dereference en legitimt fraværende værdi.
Fejl opstår efter en refactor
Tjek egenskabs- og prop-navne
Et omdøbt felt kan få et tidligere gyldigt array til at blive undefined.
Almindelige tilfælde, der ligner den samme fejl
API'en returnerer { items: [...] }, men komponenten forventer et array
Undersøg Network-svaret og tildel array-egenskaben i stedet for wrapper-objektet. Gæt ikke formen ud fra et gammelt eksempel eller et TypeScript-interface, hvis det live-svar afviger.
Med response.data.items.map(...) kan enhver manglende mellemliggende egenskab få det til at fejle. Optional chaining som response?.data?.items er nyttigt til at læse usikre indlejrede værdier, men validér den endelige samling, før du behandler den som et array.
Den første rendering sker, før et fetch Effect er færdigt
React Effects kører efter rendering. Reacts dokumentation bemærker også, at manuel datahentning inde i Effects er almindeligt i klientapplikationer, men kan have ulemper såsom vandfald, manglende server-renderede data og manuelt cache-/race-condition-arbejde. Hvis du bruger et React-framework, kan dets indbyggede dataindlæsningsmekanisme være et bedre arkitektonisk valg. React useEffect reference.
En sikrere endelig version af den illustrerende komponent
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>
);
}
Denne version skelner bevidst mellem “ikke indlæst”, “fejlet”, “tom” og “har data”. Det er mere omfattende end et enkelt optional-chain-udtryk, men det giver brugeren et meningsfuldt UI for hver tilstand og gør en fejlbehæftet API-svar synlig under fejlfinding.
Hurtig tjekliste, før du overvejer fejlen løst
Identificér den præcise værdi umiddelbart før .map().
Bekræft dens værdi og type på den rendering, der fejler.
Initialisér liste-state med [], når et tomt array nøjagtigt repræsenterer starttilstanden.
Tjek om API'en returnerer et array direkte eller pakker det ind i en objekt-egenskab.
Validér eksterne data, før du gemmer dem i state, når kontrakten er vigtig.
Brug en indlæsningsstate, hvis undefined betyder “ikke indlæst endnu”.
Brug optional chaining, når fravær er legitimt, ikke som en erstatning for at forstå dårlige data.
Tjek omdøbte eller udeladte props efter refactoring.
Giv renderede liste-elementer stabile nøgler fra dataene, som anbefalet i Reacts dokumentation for liste-rendering.
For den hypotetiske produktliste var den underliggende årsag, at den første rendering modtog undefined, hvor komponenten straks forventede et array. I en reel applikation kan den samme fejlmeddelelse komme fra state-initialisering, props, indlejrede egenskaber eller API-data. Løs datakontrakten først, og vælg derefter en renderingsguard, der nøjagtigt repræsenterer, hvad “mangler” betyder i dit UI.