Sākums
» Pamatzināšanas
»
Kā novērst kļūdu “Cannot Read Properties of Undefined (Reading map)” React vidē
Kā novērst kļūdu “Cannot Read Properties of Undefined (Reading map)” React vidē
React kļūda Cannot read properties of undefined (reading 'map') nozīmē, ka JavaScript mēģināja izpildīt kodu, piemēram, items.map(...), kamēr items bija undefined (nenoteikts). Svarīgākā daļa nav vārds map; tā ir vērtība, kas atrodas tieši pirms .map().
Pašreizējā React dokumentācija (React 19.2 rakstīšanas brīdī) joprojām izmanto JavaScript masīva map() metodi kā standarta veidu, kā pārvērst kolekcijas JSX elementos. MDN definē Array.prototype.map() kā metodi, kas izveido jaunu masīvu, piemērojot atgriezenisko zvanu katram elementam. Ja vērtība, kuru sagaidāt esam masīvu, nav inicializēta, vēl nav ielādēta vai nāk no negaidītas API struktūras, izsaukums var neizdoties, pirms React spēj renderēt sarakstu. Skatiet React ceļvedi sarakstu renderēšanā un MDN Array.prototype.map() atsauces dokumentāciju.
Ilustratīvs scenārijs: produktu saraksts, kas sabrūk, pirms API ir pabeidzis darbību
Tas ir hipotētisks piemērs skaidrojumam, nevis reāls testa rezultāts. Iedomājieties mazu veikala komponenti, kas ielādē produktus pēc tam, kad komponente pirmo reizi parādās. Izstrādātājs raksta:
Pirmajā renderēšanas reizē products ir undefined, jo useState netika nodota sākotnējā vērtība. React useState dokumentācija norāda, ka stāvokļa vērtība pirmajā renderēšanā atbilst jūsu norādītajam sākotnējam stāvoklim. Pieprasījums tiek veikts vēlāk, tāpēc renderēšana var sasniegt products.map(...), pirms atbilde ir saņemta. React useState atsauce.
AI ģenerēta ilustrācija par JavaScript kļūdu, kas norāda uz saraksta renderēšanu, kurā tiek izsaukts .map() uz nenoteiktas vērtības.
1. solis: Atrodiet precīzi, kura vērtība ir undefined
Sāciet ar steka izsekošanu (stack trace) un atrodiet rindu, kurā ir .map(). Ilustratīvajā komponentē šī rinda ir products.map(...), tāpēc products ir pirmā vērtība, kas jāpārbauda. Lielākā komponentē kļūdainā izteiksme varētu būt data.items.map(), props.users.map() vai response.results.map().
Izmantojiet pārlūka atkļūdotāju vai pagaidu žurnalizēšanu tieši pirms renderēšanas loģikas:
Tas ļauj atšķirt vairākas kļūdas, kas UI izskatās līdzīgas. Ja vērtība ir undefined, izpētiet inicializāciju vai trūkstošu īpašību. Ja tā ir null, jūsu ielādes/datu modelis var apzināti izmantot null. Ja tā ir objekts, iespējams, jūs lasāt nepareizu API atbildes līmeni. Ja tā ir virkne vai skaitlis, augšējā datu līguma struktūra atšķiras no tā, ko komponente sagaida.
Nemainiet automātiski katru kļūdaino izteiksmi ar neobligātu ķēdēšanu (optional chaining), pirms nesaprotat vērtību. Tas var nomākt sabrukumu, atstājot nepareizu datu plūsmu.
2. solis: Inicializējiet kolekcijas stāvokli kā kolekciju, ja tas atbilst jūsu modelim
Hipotētiskajam produktu sarakstam tukšs masīvs ir saprātīgs sākotnējais stāvoklis, jo “produkti vēl nav ielādēti” var droši attēlot kā kolekciju ar null elementiem:
const [products, setProducts] = useState([]);
Tagad pirmajā renderēšanā var izpildīt products.map(...), jo tukšam masīvam ir map metode. Kad pieprasījums vēlāk atjaunina stāvokli, React atkārtoti renderē ar atgrieztajiem produktiem.
AI ģenerēta ilustrācija par saraksta stāvokļa inicializāciju ar useState([]), lai pirmajā renderēšanā būtu masīvs.
Tas ir spēcīgs risinājums, ja stāvoklis konceptuāli vienmēr ir masīvs. Tas ir mazāk piemērots, ja undefined vai null nes nozīmīgu informāciju, piemēram, “vēl nav pieprasīts”, kamēr [] nozīmē “pieprasījums pabeigts un rezultātu nav”. Šādā gadījumā saglabājiet stāvokļus atšķirīgus un skaidri renderējiet ielādes, kļūdu un tukšuma nosacījumus.
3. solis: Pārbaudiet API atbildes struktūru pirms tās ievietošanas stāvoklī
Inicializācija novērš tikai pirmo renderēšanu. Tā neaizsargās komponenti, ja serveris galu galā atgriež citu struktūru. Pieņemsim, ka API patiesībā atgriež:
Tad setProducts(data) saglabā objektu, nevis masīvu. Visticamākā kļūda mainās uz kaut ko tādu kā products.map is not a function. Pareizā piešķiršana būtu setProducts(data.products), ja ir garantēts, ka šī īpašība ir masīvs.
Datiem, kas šķērso ārēju robežu, validējiet tos:
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 ģenerēta ilustrācija par saraksta renderēšanas aizsardzību ar Array.isArray() pirms .map() izmantošanas.
Tas ir svarīgi, jo “droša noklusējuma vērtība” nedrīkst pārvērst nepareizus ražošanas datus par klusi tukšu lapu. Ja saskaņā ar līgumu ir nepieciešams masīvs, kļūdas žurnalizēšana vai parādīšana var būt noderīgāka nekā katras negaidītās atbildes pārvēršana par [].
4. solis: Izvēlieties pareizo renderēšanas aizsardzību datiem, kuru var legitīmi trūkt
React atbalsta parasto JavaScript nosacīto renderēšanu. Oficiālajā dokumentācijā parādīts, kā izmantot if, ternāros izteikumus un &&, lai noteiktu, kādu JSX atgriezt. React nosacītās renderēšanas ceļvedis.
Hipotētiskajai produktu lapai skaidra ielādes un kļūdu plūsma bieži vien ir saprotamākā:
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>
);
Neobligāta ķēdēšana ir derīga arī tad, ja “pagaidām nav pieejams” vienkārši nedrīkst radīt kartētu rezultātu:
MDN paskaidro, ka neobligāta ķēdēšana (?.) pārtrauc izpildi, ja vērtība tās kreisajā pusē ir null vai undefined, tā vietā, lai izmestu TypeError. MDN neobligātās ķēdēšanas atsauce.
AI ģenerēta ilustrācija par neobligātu ķēdēšanu ar users?.map(...) datiem, kas var īslaicīgi būt undefined.
Ierobežojums ir svarīgs: products?.map(...) novērš šo konkrēto nullish piekļuves kļūdu, taču tas nepierāda, ka products ir masīvs. Ja products kļūst par objektu, products?.map joprojām atrisinās uz nenoteiktu īpašību, un mēģinājums to izsaukt var neizdoties. Izmantojiet shēmas validāciju vai Array.isArray(), ja pats datu tips ir neskaidrs.
Kuru risinājumu izvēlēties?
Situācija
Labākais pirmais solis
Kāpēc
Stāvoklim vienmēr jābūt sarakstam
useState([])
Sniedz pirmajai renderēšanai pareizo datu tipu.
Trūkstošiem datiem ir nozīmīgs ielādes stāvoklis
Nosacīta renderēšana
Saglabā “nav ielādēts” atšķirīgu no “ielādēts, bet tukšs”.
API struktūra var mainīties vai būt nepareiza
Validējiet ar Array.isArray() vai shēmu
Novērš sliktu ārēju datu iekļūšanu komponentes stāvoklī nepamanīti.
Prop ir pēc dizaina neobligāts
Neobligāta ķēdēšana vai noklusējuma prop vērtība
Izvairās no leģitīmi trūkstošas vērtības atsauces atšifrēšanas.
Kļūda parādās pēc refaktora
Pārbaudiet īpašību un prop nosaukumus
Pārdēvēts lauks var padarīt agrāk derīgu masīvu par undefined.
Bieži gadījumi, kas izskatās pēc tās pašas kļūdas
API atgriež { items: [...] }, bet komponente sagaida masīvu
Pārbaudiet tīkla atbildi un piešķiriet masīva īpašību, nevis ietvarojošo objektu. Nespējiet struktūru pēc veca piemēra vai TypeScript interfeisa, ja dzīvā atbilde atšķiras.
Izmantojot response.data.items.map(...), jebkura trūkstoša starpposma īpašība var izraisīt kļūdu. Neobligāta ķēdēšana, piemēram, response?.data?.items, ir noderīga neskaidru ligzdotu vērtību lasīšanai, taču validējiet galīgo kolekciju, pirms uzskatāt to par masīvu.
Pirmā renderēšana notiek pirms fetch Effect pabeigšanas
React Effects tiek izpildīti pēc renderēšanas. React dokumentācija arī norāda, ka manuāla datu ielādēšana Effects iekšienē ir izplatīta klienta puses lietotnēs, taču tai var būt trūkumi, piemēram, ūdenskritumi, trūkstoši servera renderēti dati un manuāla kešēšanas/sacensību apstrāde. Ja izmantojat React ietvaru, tā iebūvētais datu ielādes mehānisms var būt labāks arhitektūras izvēles punkts. React useEffect atsauce.
Drošāka ilustratīvās komponentes gala versija
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>
);
}
Šī versija apzināti atšķir “nav ielādēts”, “neizdevās”, “tukšs” un “ir dati”. Tas ir apjomīgāk nekā viena neobligātas ķēdēšanas izteiksme, taču tas sniedz lietotājam nozīmīgu UI katram stāvoklim un padara nepareizu API atbildi redzamu atkļūdošanas laikā.
Ātra pārbaudes saraksta pirms uzskatāt, ka kļūda ir novērsta
Identificējiet precīzo vērtību tieši pirms .map().
Apstipriniet tās vērtību un tipu renderēšanā, kas neizdodas.
Inicializējiet saraksta stāvokli ar [], ja tukšs masīvs precīzi attēlo sākotnējo stāvokli.
Pārbaudiet, vai API atgriež masīvu tieši vai ietver to objekta īpašībā.
Validējiet ārējos datus pirms to saglabāšanas stāvoklī, ja līgums ir svarīgs.
Izmantojiet ielādes stāvokli, ja undefined nozīmē “vēl nav ielādēts”.
Izmantojiet neobligātu ķēdēšanu, ja trūkums ir leģitīms, nevis kā aizstājēju sliktu datu izpratnei.
Pārbaudiet pārdēvētus vai izlaistus props pēc refaktoriem.
Nodrošiniet renderētajiem saraksta elementiem stabilas atslēgas no datiem, kā ieteicams React sarakstu renderēšanas dokumentācijā.
Hipotētiskajam produktu sarakstam pamatcēlonis bija pirmā renderēšana, kas saņēma undefined, kur komponente nekavējoties sagaidīja masīvu. Reālā lietotnē tā pati kļūdas ziņa var rasties no stāvokļa inicializācijas, props, ligzdotām īpašībām vai API datiem. Vispirms novērsiet datu līguma problēmu, pēc tam izvēlieties renderēšanas aizsardzību, kas precīzi attēlo, ko “trūkstošs” nozīmē jūsu UI.