Search code examples
vuejs3reactivevue-composition-api

Reactive object assignment from reactive array


I need to assign to a reactive object a object from a reactive array inside script setup composition API, any suggestion?

Here is my code - the variable pokemonsReactiveArr is:

export let pokemonsReactiveArr = ref<IPokemon[]>([]);

This array after the await call it contains 4 pokemons:

<script setup lang="ts">
import { onMounted, ref } from "vue";
import PokemonPicture from "../components/PokemonPicture.vue";
import PokemonsOptions from "../components/PokemonsOptions.vue";
import { getPokemonsOptions } from "../helpers/getPokemonOptions";
import { pokemonsReactiveArr } from "../helpers/getPokemonOptions";

let pokemons = ref<any[]>([]);
let selectedPokemon = ref<any>({});
let idPokemon = ref<number>(0);

defineExpose({ selectedPokemon });

onMounted(async () => {
  console.log(
    "array antes de llamar al helper de pokemons en Home view: ",
    pokemonsReactiveArr
  );
  await getPokemonsOptions();
  const rndInt = Math.floor(Math.random() * 4);
  idPokemon.value = rndInt;
  console.log(
    "array despues del llamar al helper de pokemons en Home view: ",
    pokemonsReactiveArr
  );
  console.log('el pokemon seleccionado es:',pokemonsReactiveArr.value[rndInt])
  console.log("pokemon seleccionado antes: ", selectedPokemon.value);
  selectedPokemon.value = pokemonsReactiveArr.value[rndInt];
  console.log("pokemon seleccionado despues: ", selectedPokemon.value);
  console.log("id del pokemon despues de la asignacion: ", idPokemon.value);
});
</script>

<template>
  <v-container>
    <v-row justify="center">
      <!--
        <h1 v-if="!selectedPokemon">Cargando pokemon...</h1>
      -->
      <div>
        <PokemonPicture :pokemonId="idPokemon" :show="true" />
      </div>
    </v-row>
    <v-row v-if="pokemons" justify="center">
      <PokemonsOptions :pokemons="pokemonsReactiveArr" />
    </v-row>
  </v-container>
</template>

When I print in console the selected Pokemon it shows a empty proxy object but after assignment is undefined, how can I achieve the desired effect? Here I share the logic to get the array of pokemons:

import { ref } from "vue";
import pokemonAPI from "../api/pokemonApi";
import { IPokemon } from "../interface/IPokemon";

export let pokemonsReactiveArr = ref<IPokemon[]>([]);

const getPokemons = () => {
  const pokemonsArr = Array.from(Array(650));
  return pokemonsArr.map((_, index) => index + 1);
};

export const getPokemonsOptions = async () => {
  const mixPokemons = getPokemons().sort(() => Math.random() - 0.5);
  await getPokemonsName(mixPokemons.splice(0, 4));
};

const getPokemonsName = async (pokemonsArr: number[]) => {
  
  pokemonsArr.forEach(async (pokemonID: number) => {
    const response = await pokemonAPI.get(`/${pokemonID}`);
    pokemonsReactiveArr.value.push({ name: response.data.name, id: response.data.id });
  });
};

enter image description here


Solution

  • At the end I managed to get the behavior that I wanted through this code:

    import { ref } from "vue";
    import pokemonAPI from "../api/pokemonApi";
    import { IPokemon } from "../interface/IPokemon";
    
    export let pokemonsReactiveArr = ref<IPokemon[]>([]);
    export let pokemonSelected = ref<IPokemon>({ id: 0, name: "" });
    
    const getPokemons = () => {
      const pokemonsArr = Array.from(Array(650));
      return pokemonsArr.map((_, index) => index + 1);
    };
    
    export const getPokemonsOptions = async () => {
      const mixPokemons = getPokemons().sort(() => Math.random() - 0.5);
      await getPokemonsName(mixPokemons.splice(0, 4));
    };
    
    const getPokemonsName = async (pokemonsArr: number[]) => {
      pokemonsArr.forEach(async (pokemonID: number) => {
        const response = await pokemonAPI.get(`/${pokemonID}`);
        if (pokemonsReactiveArr.value.length == 4) {
        } else {
          pokemonsReactiveArr.value.push({
            name: response.data.name,
            id: response.data.id,
          });
          if (pokemonsReactiveArr.value.length == 4) {
            pokemonSelected.value =
              pokemonsReactiveArr.value[Math.floor(Math.random() * 4)];
          }
        }
      });
    };
    

    And in the HomewView.vue:

    <script setup lang="ts">
    import { onMounted, ref } from "vue";
    import PokemonPicture from "../components/PokemonPicture.vue";
    import PokemonsOptions from "../components/PokemonsOptions.vue";
    import { getPokemonsOptions } from "../helpers/getPokemonOptions";
    import {
      pokemonsReactiveArr,
      pokemonSelected,
    } from "../helpers/getPokemonOptions";
    import { IPokemon } from "../interface/IPokemon";
    import { useGeneralStore } from "../stores/GeneralStore.ts";
    
    const store = useGeneralStore();
    
    let showDialog = ref<boolean>();
    let answerMessage = ref<string>();
    let showVictory = ref<boolean>(false);
    let done = ref<boolean>(false);
    
    onMounted(async () => {
      await getPokemonsOptions();
    });
    
    async function checkRespuesta(pokemonClicked: IPokemon) {
      if (pokemonSelected.value === pokemonClicked) {
        store.acierto();
        answerMessage.value = "¡Has acertado!";
        showDialog.value = true;
        showVictory.value = true;
        done.value = true;
      } else {
        store.reiniciaJuego();
        answerMessage.value = "¡Has fallado!";
        showDialog.value = true;
        showVictory.value = false;
        done.value = true;
      }
    }
    
    function cerrar() {
      showDialog.value = false;
    }
    
    async function reiniciar() {
      pokemonsReactiveArr.value = [];
      pokemonSelected.value = { id: 0, name: "" };
      showVictory.value = false;
      done.value = false;
      await getPokemonsOptions();
    }
    </script>
    
    <template>
      <v-container>
        <template v-if="done">
          <v-row justify="center">
            {{ answerMessage?.toLocaleUpperCase() }}
          </v-row>
          <v-row justify="center">
            <v-col cols="6">
              <v-text-field
                v-if="showVictory"
                color="black"
                single-line
                variant="solo-filled"
                readonly
              >
                <h2>{{ pokemonSelected.name }}</h2>
              </v-text-field>
            </v-col>
            <PokemonPicture :pokemonId="pokemonSelected.id" :show="showVictory" />
          </v-row>
          <v-row justify="center">
            <v-btn @click="reiniciar">Reiniciar</v-btn>
          </v-row>
        </template>
        <template v-else>
          <v-row justify="center">
            <h1 v-if="!pokemonSelected">Cargando pokemon...</h1>
    
            <div v-else>
              <div v-if="pokemonSelected.id != 0">
                <h1>¿Quién es este pokemon?</h1>
                <PokemonPicture
                  :pokemonId="pokemonSelected.id"
                  :show="showVictory"
                />
              </div>
            </div>
          </v-row>
          <v-row justify="center">
            <PokemonsOptions
              :pokemons="pokemonsReactiveArr"
              @pokemonmClickado="checkRespuesta($event)"
            />
          </v-row>
        </template>
    
        <v-dialog v-model="showDialog" persistent width="auto">
          <v-card>
            <v-card-title>
              {{ answerMessage }}
            </v-card-title>
            <v-card-subtitle> tienes {{ store.getPoints }} punto/s </v-card-subtitle>
            <v-card-actions>
              <v-row justify="center">
                <v-btn @click="cerrar">OK</v-btn>
              </v-row>
            </v-card-actions>
          </v-card>
        </v-dialog>
      </v-container>
    </template>
    

    I hope this'd be util for someone