Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeError: Cannot read property 'url' of undefined / React App crashing

Tags:

reactjs

I have an issue where my app crashes upon searching. I suspect this is because at some point during rendering the artist container is trying to render the code but there is no image url even so when I put a backup URL e.g. src={props.artist.images[0].url || "backup url"} it still fails. If y try to just load artist name I have no issues but the image is the Achilles heel here. I have spent a lot of time trying to figure out why it is crashing but to no avail.

Also artist container should only be rendering if we do have artist results ... so a bit confused as to why it is saying URL not defined. 99% of these results have images.

const [query, setQuery] = useState("")
const [artistResults, setArtistResults] = useState(null)
        
const fetchQueryArtists = async () => {
    try {
        let response = await fetch(`${ApiUrl}/search?q=${query}&type=artist`, {
            headers: {
                "Authorization": "Bearer " + props.token
            }
        })
    
        if (response.ok) {
            let json = await response.json()
            console.log(json.artists.items)
            setArtistResults(json.artists.items)
        }
    } catch (error) {
        console.log(error)
    }
}

useEffect(() => {
    if (query.length > 1) {
        fetchQueryArtists()
        fetchQueryTracks()
        fetchQueryAlbums()
    }
}, [query])

Inside my return is this:

{query.length > 0 && artistResults &&
                        <h3 className="py-3 px-2">Artists</h3>}
    
{artistResults && artistResults.length > 0 ? artistResults.slice(0, 6).map(artist => {
    return <ArtistContainer key={artist.id} artist={artist} />
}) : <h1>Test flag</h1>}

This is my artist container:

return (
    <div onClick={directToPlaylist} className="category-container d-flex align-items-center justify-content-center  mb-4 col-6 col-md-4 col-lg-3 col-xl-2">
        <img  className="img-fluid" height={150} src={props.artist.images[0].url} alt="playlist-cover" />
        <div>
            <span>{props.artist.name}</span>
            <p className="text-muted">Artist</p>
        </div>             
    </div>
);

This is the JSON (just in case you think I am accessing the images incorrectly):

Array(20)
0:
external_urls: {spotify: "https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4"}
followers: {href: null, total: 55327781}
genres: (6) ["canadian hip hop", "canadian pop", "hip hop", "pop rap", "rap", "toronto rap"]
href: "https://api.spotify.com/v1/artists/3TVXtAsR1Inumwj472S9r4"
id: "3TVXtAsR1Inumwj472S9r4"
images: Array(3)
0: {height: 640, url: "https://i.scdn.co/image/60cfab40c6bb160a1906be45276829d430058005", width: 640}
1: {height: 320, url: "https://i.scdn.co/image/5ea794cf832550943d5f8122afcf5f23ee9d85b7", width: 320}
2: {height: 160, url: "https://i.scdn.co/image/8eaace74aaca82eaccde400bbcab2653b9cf86e1", width: 160}
length: 3
__proto__: Array(0)
name: "Drake"
popularity: 98
type: "artist"
uri: "spotify:artist:3TVXtAsR1Inumwj472S9r4"
__proto__: Object
like image 577
mycodegoesKABOOM Avatar asked Jan 18 '26 12:01

mycodegoesKABOOM


1 Answers

Use this:

src={props.artist.images[0]?.url || "backup url"}

I've use something called as optional chaining here. You can read more about it here

Detailed Explanation:

  1. Before the api call, the react renders the component, that time: props.artist.images = [].

  2. So before we fetch the data, it would be an empty array, accessing first element of that gives undefined.

  3. Therefore, you try to access undefined.url, this gives an error.

Now more about optional chaining:

The optional chaining operator (?.) enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.

Traditionally if we have to safely access nested elements, we do:

const obj = {
  name: {
    first: "foo",
    last: "bar"
  }
};
// Getting first element
const firstName = obj && obj.name && obj.name.first ? obj.name.first : "fallback";

// optional chaining
const firstElement = obj?.name?.first || "fallback"

Summarising example:

// This raises TypeError
console.log(undefined.someKey)

// Optinal chaining gives safety net and does not give TypeError
console.log(undefined?.somKey);

About Browser Support and Usage: This may not work with older browser due to compatibility issue. Check here

Also note, if you're using Typescript > 3.7 then optional chaining can work for older browsers as well (since Babel accordingly transpiles to target the configured browsers).

like image 67
Shravan Dhar Avatar answered Jan 20 '26 00:01

Shravan Dhar