Etusivu
» Perustieto
»
Miten korjata virhe “Cannot Read Properties of Undefined (Reading map)” Reactissa
Miten korjata virhe “Cannot Read Properties of Undefined (Reading map)” Reactissa
React-virhe Cannot read properties of undefined (reading 'map') tarkoittaa, että JavaScript yritti arvioida lauseketta kuten items.map(...), kun items oli undefined. Tärkeä osa ei ole sana map, vaan arvo, joka on välittömästi .map():n edessä.
Nykyinen React-dokumentaatio (React 19.2 kirjoitushetkellä) käyttää edelleen JavaScriptin taulukon map()-metodia standarditapana muuntaa kokoelmia JSX-elementeiksi. MDN määrittelee Array.prototype.map():n menetelmäksi, joka luo uuden taulukon soveltamalla callback-funktiota jokaiseen alkioon. Jos arvo, jonka odotat olevan taulukko, ei ole alustettu, ei ole vielä latautunut tai tulee odottamattomasta API-rakenteesta, kutsu voi epäonnistua ennen kuin React ehtii renderöidä listan. Katso Reactin opas listojen renderöintiin ja MDN:n Array.prototype.map()-viite.
Esimerkkitilanne: tuotelista, joka kaatuu ennen kuin API on valmis
Tämä on hypoteettinen esimerkki selitystä varten, ei todellinen testitulos. Kuvittele pieni verkkokaupan komponentti, joka hakee tuotteet komponentin ensimmäisen näkymän jälkeen. Kehittäjä kirjoittaa:
Ensimmäisessä renderöinnissä products on undefined, koska useState:lle ei annettu alkuarvoa. Reactin useState-dokumentaatio toteaa, että tilan arvo ensimmäisessä renderöinnissä vastaa antamaasi alkutilaa. Fetch-suoritus tapahtuu myöhemmin, joten renderöinti voi saavuttaa kohdan products.map(...) ennen kuin vastaus saapuu. React useState -viite.
Tekoälyn luoma kuvitus JavaScript-virheestä, joka osoittaa listan renderöintiin, jossa kutsutaan .map() undefined-arvolle.
Vaihe 1: Etsi tarkasti, mikä arvo on undefined
Aloita pinon jäljityksestä (stack trace) ja etsi rivi, jossa on .map(). Esimerkkikomponentissa tämä rivi on products.map(...), joten products on ensimmäinen arvo, joka tulee tutkia. Suuremmassa komponentissa epäonnistuva lauseke voi olla esimerkiksi data.items.map(), props.users.map() tai response.results.map().
Käytä selaimen debuggeria tai väliaikaista lokitusta välittömästi ennen renderöintilogiikkaa:
Tämä erottaa useita virheitä, jotka näyttävät samalta käyttöliittymässä. Jos arvo on undefined, tutki alustusta tai puuttuvaa ominaisuutta. Jos se on null, lataus-/datamallisi saattaa käyttää nolla-arvoa eksplisiittisesti. Jos se on olio, saatat lukea väärää tasoa API-vastauksesta. Jos se on merkkijono tai numero, ylävirran datasopimus eroaa siitä, mitä komponentti odottaa.
Älä automaattisesti korvaa jokaista epäonnistuvaa lauseketta valinnaisella ketjutuksella (optional chaining) ennen kuin ymmärrät arvon. Se voi peittää kaatumisen jättäen virheellisen tietovirran ennalleen.
Vaihe 2: Alusta kokoelman tila kokoelmaksi, jos se vastaa malliasi
Hypoteettiselle tuotelistalle tyhjä taulukko on järkevä alkutila, koska ”ei vielä ladattuja tuotteita” voidaan turvallisesti esittää nollakohtaisena kokoelmana:
const [products, setProducts] = useState([]);
Nyt ensimmäinen renderöinti voi suorittaa products.map(...), koska tyhjällä taulukolla on map-metodi. Kun pyyntö myöhemmin päivittää tilan, React renderöi uudelleen palautettujen tuotteiden kanssa.
Tekoälyn luoma kuvitus listan tilan alustamisesta komennolla useState([]), jotta ensimmäisessä renderöinnissä on taulukko.
Tämä on vahva korjaus, kun tila on käsitteellisesti aina taulukko. Se on vähemmän sopiva, kun undefined tai null kantaa merkityksellistä tietoa, kuten ”ei vielä pyydetty”, kun taas [] tarkoittaa ”pyyntö valmistui ja tuloksia on nolla”. Tällöin pidä tilat erillään ja renderöi lataus-, virhe- ja tyhjät ehdot eksplisiittisesti.
Vaihe 3: Tarkista API-vastauksen rakenne ennen kuin sijoitat sen tilaan
Alustus korjaa vain ensimmäisen renderöinnin. Se ei suojaa komponenttia, jos palvelin lopulta palauttaa eri rakenteen. Oletetaan, että API palauttaa:
Tällöin setProducts(data) tallentaa olion, ei taulukkoa. Todennäköinen virhe muuttuu muotoon products.map is not a function. Oikea sijoitus olisi setProducts(data.products), edellyttäen että tämä ominaisuus on taattu olevan taulukko.
Ulkorajan yli kulkevalle datalle, validoi se:
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);
});
Tekoälyn luoma kuvitus listan renderöinnin suojaamisesta komennolla Array.isArray() ennen .map():n käyttöä.
Tämä on tärkeää, koska ”turvallisen oletuksen” ei pitäisi muuttaa virheellistä tuotantodataa hiljaa tyhjäksi sivuksi. Jos sopimus vaatii taulukkoa, virheen lokitus tai näkyväksi tekeminen voi olla hyödyllisempää kuin muuntaa jokainen odottamaton vastaus muotoon [].
Vaihe 4: Valitse oikea renderöintisuoja datalle, joka voi olla perustellusti puuttuva
React tukee normaalia JavaScriptin ehdollista renderöintiä. Virallisessa dokumentaatiossa näytetään, miten käyttää if-lauseita, ternääri-ilmauksia ja &&-operaattoria päättämään, mitä JSX:tä palautetaan. Reactin ehdollisen renderöinnin opas.
Hypoteettiselle tuotesivulle eksplisiittinen lataus- ja virhekulku on usein selkein:
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>
);
Valinnainen ketjutus on myös kelvollinen, kun ”puuttuu toistaiseksi” pitäisi yksinkertaisesti tuottaa ei-mappattua tulosta:
MDN selittää, että valinnainen ketjutus (?.) keskeyttää suorituksen, kun sen vasemmalla puolella oleva arvo on null tai undefined, sen sijaan että heittäisi TypeError-virheen. MDN:n valinnaisen ketjutuksen viite.
Tekoälyn luoma kuvitus valinnaisesta ketjutuksesta users?.map(...) datalle, joka voi tilapäisesti olla undefined.
Rajoitus on tärkeä: products?.map(...) estää tämän tietyn nullish-access-virheen, mutta se ei todista, että products on taulukko. Jos products muuttuu olioksi, products?.map ratkeaa silti undefined-ominaisuudeksi, ja sen kutsu voi epäonnistua. Käytä skeemavaliidaatiota tai Array.isArray(), kun tietotyyppi itsessään on epävarma.
Millaisen korjauksen sinun tulisi valita?
Tilanne
Paras ensimmäinen askel
Miksi
Tilan tulisi aina olla lista
useState([])
Antaa ensimmäiselle renderöinnille oikean tietotyypin.
Puuttuvalla datalla on merkityksellinen lataustila
Ehdollinen renderöinti
Pitää ”ei ladattu” erillään tilasta ”ladattu mutta tyhjä”.
API:n rakenne voi vaihdella tai olla virheellinen
Validoi komennolla Array.isArray() tai skeemalla
Estää huonoa ulkoista dataa pääsemästä komponentin tilaan huomaamatta.
Prop on suunniteltu valinnaiseksi
Valinnainen ketjutus tai oletusprop-arvo
Välttää perustellusti puuttuvan arvon dereferenssin.
Virhe ilmenee refaktoroinnin jälkeen
Tarkista ominaisuus- ja prop-nimet
Uudelleennimetty kenttä voi tehdä aiemmin kelvollisesta taulukosta undefined.
Yleiset tapaukset, jotka näyttävät samalta virheeltä
API palauttaa { items: [...] }, mutta komponentti odottaa taulukkoa
Tutki Network-vastausta ja sijoita taulukko-ominaisuus peittävä olion sijaan. Älä arvaile rakennetta vanhasta esimerkistä tai TypeScript-rajapinnasta, jos live-vastaus eroaa.
Lausekkeessa response.data.items.map(...) mikä tahansa puuttuva välillinen ominaisuus voi aiheuttaa virheen. Valinnainen ketjutus kuten response?.data?.items on hyödyllinen epävarmojen sisäkkäisten arvojen lukemiseen, mutta validoi lopullinen kokoelma ennen kuin käsittelet sitä taulukkona.
Ensimmäinen renderöinti tapahtuu ennen kuin fetch-Effect on valmis
React Effects suoritetaan renderöinnin jälkeen. Reactin dokumentaatio huomauttaa myös, että datan manuaalinen hakeminen Effectsien sisällä on yleistä asiakaspuolen sovelluksissa, mutta sillä voi olla haittoja kuten vesiputoukset (waterfalls), puuttuva palvelinpuolen renderöity data ja manuaalinen välimuistin/kilpailutilanteiden hallinta. Jos käytät React-kehystä, sen sisäänrakennettu datan latausmekanismi voi olla parempi arkkitehtoninen valinta. React useEffect -viite.
Turvallisempi lopullinen versio esimerkkikomponentista
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>
);
}
Tämä versio erottaa tarkoituksellisesti tilat ”ei ladattu”, ”epäonnistui”, ”tyhjä” ja ”dataa olemassa”. Se on puhekielisempi kuin yksittäinen valinnainen ketjutuslauseke, mutta se antaa käyttäjälle merkityksellisen käyttöliittymän jokaiselle tilalle ja tekee virheellisestä API-vastauksesta näkyvän debuggauksen aikana.
Pikalistatarkistus ennen kuin harkitset virheen korjatuksi
Tunnista tarkka arvo välittömästi .map():n edessä.
Varmista sen arvo ja tyyppi epäonnistuvassa renderöinnissä.
Alusta listan tila arvolla [], kun tyhjä taulukko kuvaa alkutilaa tarkasti.
Tarkista, palauttaako API taulukon suoraan vai kääriikö sen olion ominaisuuteen.
Validoi ulkoinen data ennen sen tallentamista tilaan, kun sopimus on tärkeä.
Käytä lataustilaa, jos undefined tarkoittaa ”ei vielä ladattu”.
Käytä valinnaista ketjutusta, kun puuttuminen on perusteltua, ei korvikkeena huonon datan ymmärtämiselle.
Tarkista uudelleennimettyjä tai jätettyjä proppeja refaktorointien jälkeen.
Anna renderöidyille listakohteille vakaat avaimet datasta, kuten Reactin listojen renderöintidokumentaatio suosittelee.
Hypoteettiselle tuotelistalle juurisyy oli se, että ensimmäinen renderöinti sai undefined-arvon, jossa komponentti odotti välittömästi taulukkoa. Todellisessa sovelluksessa sama virheilmoitus voi johtua tilan alustuksesta, proppeista, sisäkkäisistä ominaisuuksista tai API-datasta. Korjaa datasopimus ensin, valitse sitten renderöintisuoja, joka kuvaa tarkasti, mitä ”puuttuminen” tarkoittaa käyttöliittymässäsi.