3

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

2 Answers 2

2

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).

Sign up to request clarification or add additional context in comments.

5 Comments

this works friend. thanks a bunch. I am a bit confused as to why my check wasn't working though. Reading the docs on null collescing but don't understand it
Give me a min, I'll explain bit more in my example @mycodegoesKABOOM
ok no problem. thanks a lot for taking time to explain.
I've done edit, the feature is optional chaining and not null coalescing.
"Before the api call, the react renders the component, that time: props.artist.images = []." The artist container should not render until we have the data back from the api though due to this check " {artistResults && artistResults.length > 0 ? artistResults.slice(0, 6).map(artist => { return <ArtistContainer key={artist.id} artist={artist} /> }) : <h1>Test flag</h1>}". At that point we would have the images so what am I missing here?
0

Try

props.artist[0].images[0]?.url || "backup url"

instead of

props.artist.images[0].url

1 Comment

thanks Harsh, this syntax works props.artist.images[0]?.url || "backup url"

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.