Matej Bednar
Matej Bednar

Reputation: 31

Using this in lifecycle hook created from composition API

I have a problem with typescript in Vue 3.

  1. Get Data from Itunes everytime prop 'query' is modified. (format: [{}, {},{},...]
  2. Call Function to filter unused information from fetch

When I call function (this function will filter objects to simplify code) in fetch, it throws an Error:

Property 'filterData' does not exist on type 'void'.

My Code:

<template>
    <div>
    </div>
</template>

<script lang="ts">
import { defineComponent, onUpdated } from 'vue';

interface Song {
    id: number;
    name: string;
    nameT: string;
    cover?: string;
    preview: string;
}

interface SongFromITunes {
    artistId: number;
    artistName: string;
    trackName: string;
    previewUrl: string;
    artworkUrl100?: string;
}

export default defineComponent({
    data() {
        return {
            list: [] as Array<Song>
        };
    },
    props: {
        query: String
    },
    methods: {
        filterData({
            artistId: id,
            artistName: name,
            trackName: nameT,
            artworkUrl100: cover,
            previewUrl: preview
        }: SongFromITunes): Song {
            return { id, name, nameT, cover, preview };
        }
    },
    setup(props) {
        onUpdated(() => {
            const { query } = props;
            if (query != null) {
                fetch(
                    `https://itunes.apple.com/search?term=${encodeURI(
                        query
                    )}&limit=10&entity=musicTrack`
                )
                    .then((resolve) => resolve.json())
                    .then((data) => data.results.map((item: SongFromITunes) => this.filterData(item));
            }
        });
    }
});
</script>

<style scoped></style>

Upvotes: 3

Views: 1720

Answers (2)

Michal Lev&#253;
Michal Lev&#253;

Reputation: 37803

I found your question really interesting.

From the docs it's clear this cannot be used inside setup() as the Vue instance doesn't exist yet.

But in the case of lifecycle hooks you are passing the callback which must be sometime later attached to the new Vue instance

Docs : These lifecycle hook registration functions can only be used synchronously during setup(), since they rely on internal global state to locate the current active instance (the component instance whose setup() is being called right now).

So it seems the right way to do would be it would be easy if Vue just used bind and bound the callback to the instance. However it's not the case it seems...

Solution for you is to move filterData from methods into setup()

    setup(props) {
        let filterData = function({
            artistId: id,
            artistName: name,
            trackName: nameT,
            artworkUrl100: cover,
            previewUrl: preview
        }: SongFromITunes): Song {
            return { id, name, nameT, cover, preview };
        }

        onUpdated(() => {
            const { query } = props;
            if (query != null) {
                fetch(
                    `https://itunes.apple.com/search?term=${encodeURI(
                        query
                    )}&limit=10&entity=musicTrack`
                )
                    .then((resolve) => resolve.json())
                    .then((data) => data.results.map((item: SongFromITunes) => filterData(item));
            }
        });

        return { filterData }
    }

Update

I was really curious about the reasons behind this because I feel hooks are inherently bound to the instance and probably many people will expect this is available in callbacks. So I asked on Vue discord and received following explanation (thanks Vaage#9161):

If we bind the lifecycle hooks in setup to the instance, it would only cause confusion and encourage antipatterns. You make a choice, options api, or composition api. If you choose composition api, there is nothing interesting for you on this. Everything is contained in the setup closure. If we added it, typescript inference would be harder to implement, and people will start using the options api in combination with it.

Message of the day

Don't mix composition api and options api

Upvotes: 6

camwhite
camwhite

Reputation: 937

this is not accessible from the setup function because the component has not been created yet. You need to define filterData inside setup as well.

Here's the relevant documentation

Upvotes: 1

Related Questions