My elements will only appear (sometimes) after hot reloading, but not in first load

32 views Asked by At

I have a file called SpecPlayer.vue where I'm using a component (SpecChampionData) and the component will only appear after hot reloading (not always, sometimes), but not after the first load.

I'm new to Vue and I don't use TS or JS in a daily basis, so please try to answer as completely as possible, so I can understand what's actually happening here.

enter image description here

As you can see, the component is somewhat partially loaded.

This is how I'm using it:

<script setup lang="ts">
import * as art from '@/getArt'
import SpecChampionData from './SpecChampionData.vue'
import { Player } from '@/types/ddata'

defineProps<{
    player: Player | undefined
}>()
</script>
<template>
    <div class="w-full p-4 rounded-md bg-semi">
        <div class="flex flex-row gap-2 flex-nowrap">
            <div class="relative h-20 border-2 aspect-square avatar border-primary">
                <div>
                    <div v-if="player?.isDead"
                        class="absolute inset-0 z-20 flex items-center justify-center aspect-square h-full text-[#ff0000]">
                        <h1 class="text-3xl font-bold">{{ Math.ceil(player.respawnTimer || 0) }}</h1>
                    </div>
                    <img v-if="player?.championName" class="avatar" :class="{
                        'grayscale brightness-50': player?.isDead
                    }" :src="art.getChampIcon(player?.championName)">
                    <img v-else src="/BigFist.png" class="grayscale">
                    <div
                        class="absolute bottom-0 w-7 left-[50%] z-10 translate-x-[-50%] translate-y-[50%] bg-semi border-2 border-primary rounded-full aspect-square flex justify-center items-center">
                        <h1 class="text-sm font-medium text-center text-white">{{ player?.level || 0 }}</h1>
                    </div>
                </div>
            </div>
            <div class="flex flex-col">
                <h1 class="text-xl font-semibold text-primary">{{ player?.summonerName || 'Jugador' }}</h1>
                <h1 class="text-white">{{ player?.championName }} <h1 v-if="player?.skinName"> ({{ player?.skinName }})
                    </h1>
                </h1>
            </div>
        </div>
        <!-- Here is the component -->
        <SpecChampionData :player="player"></SpecChampionData>
    </div>
</template>

And this is the component itself:

<template>
    <h1>{{ champData?.lore }}</h1>
</template>
<script setup lang="ts">
import axios from 'axios'
import { Champion, ChampionResponse, Player } from '@/types/ddata'
import { Ref, ref } from 'vue'

const props = defineProps<{
    player: Player | undefined
}>()
const champData: Ref<Champion | undefined> = ref()

const champName = props.player?.championName

axios.get(`https://ddragon.leagueoflegends.com/cdn/14.5.1/data/es_AR/champion/${champName}.json`)
    .then(res => {
        const resData: ChampionResponse = res.data
        if (champName) {
            champData.value = resData.data[champName]
        }
    })
</script>

I've tried restarting the host, and using onBeforeMount/onMounted. I'm not sure what I'm doing wrong.

1

There are 1 answers

1
Stefan Mitrovic On

The fact that your component (sometimes) loads on hot reload indicates that the props.player is not being passed through properly.

So here's what happens chronologically -

  1. SpecPlayer.vue gets mounted with with props.player value being undefined, because in your parent component you didn't wait for player value to be available before calling SpecPlayer.vue.

  2. props.player with value undefined gets propagated further down to SpecChampionData.vue and axios fails because champion with name undefined doesn't exist, which is why your component doesn't load properly.

  3. While all of this is happening, player value became available but all the other components are mounted already.

  4. When you update something and it hot reloads, since player value became available in the meantime, it propagates properly and the component loads.

To combat this, make sure you only load component if you have all the necessary data for the component to work.

In the parent of SpecPlayer.vue, make sure to only load SpecPlayer component if you have player available like so -

  <SpecPlayer v-if="player" :player="player" />

This will ensure that <SpecPlayer /> component will load only if player is available.

If player is only required for SpecChampionData.vue, then move the if statement further down the chain -

  <SpecChampionData v-if="player" :player="player" />

TLDR - You didn't wait for data to be loaded before calling the component and axios is trying to fetch undefined.