I am building a Pokemon filtered search app using Vue 3, Composition API, and Pinia. I am attempting to set up the app so that the fetched response from the Pokemon API is passed to a store (set up using Pinia) inside the fetchPokemon() function.
const fetchPokemon = () => {axios.get("https://pokeapi.co/api/v2/pokemon?offset=0").then((response) => {store.addPokemon(response.data.results)})}
After passing the response to the store, the updatePokemon() function uses filter and include methods to filter out and match Pokemon in the store with Pokemon in the user-input text field ("state.text"):
const updatePokemon = () => {if(!state.text) {return []}return store.getState.pokemons.filter((pokemon) => pokemon.name.includes(state.text))}
When executing the app, I am getting the following error in the updatePokemon() function:
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'includes')
I'm assuming this means the .includes() method for searching/filter cannot be used for this search. How should I go about handling the filter and include methods to match Pokemon in the store with the user-inputted Pokemon?
Here is the code:
Pinia Store
import { defineStore } from 'pinia'export const usePokemonStore = defineStore({id: 'store',state: () => ({pokemons: []}),getters: {getState(state) {return state}},actions: {addPokemon(name) {this.pokemons.push(name)}}})
Component
<template><div class="w-full flex justify-center"><input type="text" placeholder="Enter Pokemon here" class="mt-10 p-2 border-blue-500 border-2" v-model="text"/></div><div class="mt-10 p-4 flex flex-wrap justify-center"><div class="ml-4 text-2x text-blue-400" v-for="(pokemon, idx) in filteredPokemon" :key="idx"><router-link :to="`/about/${getPokemonId(pokemon.name)}`">{{ pokemon.name }} - with id {{ getPokemonId(pokemon.name) }}</router-link></div></div></template><script>import axios from 'axios';import { reactive, toRefs, computed } from 'vue';import { usePokemonStore } from '@/store';export default {name: 'Home',setup() {const store = usePokemonStore()const state = reactive({text: "",filteredPokemon: computed(()=> updatePokemon())})const updatePokemon = () => {if(!state.text) {return []}return store.getState.pokemons.filter((pokemon) => pokemon.name.includes(state.text))}const fetchPokemon = () => {axios.get("https://pokeapi.co/api/v2/pokemon?offset=0").then((response) => {store.addPokemon(response.data.results)})}fetchPokemon()const getPokemonId = (item) => {console.log(item)return store.pokemons.findIndex((p) => p.name === item) + 1}return { ...toRefs(state), fetchPokemon, getPokemonId, updatePokemon, store }}}</script>
UPDATED
Store - with not action
import { defineStore } from 'pinia'export const usePokemonStore = defineStore({id: 'store',state: () => ({pokemons: []})})
Component - with no store.addPokemon(...)
<template><div class="w-full flex justify-center"><input type="text" placeholder="Enter Pokemon here" class="mt-10 p-2 border-blue-500 border-2" v-model="text"/></div><div class="mt-10 p-4 flex flex-wrap justify-center"><div class="ml-4 text-2x text-blue-400" v-for="(pokemon, idx) in filteredPokemon" :key="idx"><router-link :to="`/about/${getPokemonId(pokemon.name)}`">{{ pokemon.name }} - with id {{ getPokemonId(pokemon.name) }}</router-link></div></div></template><script>import axios from 'axios';import { reactive, toRefs, computed } from 'vue';import { usePokemonStore } from '@/store';export default {name: 'Home',setup() {const store = usePokemonStore()const state = reactive({// pokemons: [],text: "",filteredPokemon: computed(()=> updatePokemon())})const updatePokemon = () => {if(!state.text) {return []}return store.pokemons.filter((pokemon) => pokemon.name.includes(state.text))}const fetchPokemon = () => {axios.get("https://pokeapi.co/api/v2/pokemon?offset=0").then((response) => {store.pokemons = response.data.results})}fetchPokemon()const getPokemonId = (item) => {console.log(item)return store.pokemons.findIndex((p) => p.name === item) + 1}return { ...toRefs(state), fetchPokemon, getPokemonId, store }}}</script>
Best Answer
First of all, you don't need getState
at all.
You can use usePokemonStore().pokemons
directly. The object returned by calling usePokemonStore()
function includes:
- all state properties
- all actions
- all getters.
Here's how to get the filtered pokemon array, based on whether their name includes state.text
:
setup() {const store = usePokemonStore();const state = reactive({text: "",filteredPokemons: computed(() => store.pokemons.filter(pokemon => pokemon.name.includes(state.text)))});return {...toRefs(state)}}
Working example:
const { createApp, reactive, toRefs, computed, onMounted } = Vue;const { defineStore, createPinia } = Pinia;const usePokemons = defineStore('pokemon', {state: () => ({ pokemons: [] })});const pinia = createPinia();createApp({pinia,setup() {const store = usePokemons(pinia);const state = reactive({searchTerm: '',filteredPokemons: computed(() => store.pokemons.filter(pokemon => pokemon.name.includes(state.searchTerm)))});onMounted(() => {fetch('https://pokeapi.co/api/v2/pokemon?offset=0').then(r => r.json()).then(r => store.pokemons = r.results)});return {...toRefs(state)}}}).mount('#app')
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script><script src="https://unpkg.com/vue-demi"></script><script src="https://unpkg.com/[email protected]/dist/pinia.iife.prod.js"></script><div id="app"><input v-model="searchTerm"><div v-for="pokemon in filteredPokemons">{{ pokemon.name }}</div></div>