sintj
sintj

Reputation: 814

How to properly use the new fetch() hook in Nuxt 2.12 for SSR?

I updated Nuxt to the latest version because they introduced the new fetch() hook. In my project, I'm retrieving data from Prismic. Before, I was using asyncData(), but when navigating, it took some time before rendering the pages where asyncData() was invoked (this is how it works).

The ideal solution is to navigate on that particular page, and show a loading animation while retrieving the data. The new fetch() hook seems appropriate because it exposes the $fetchState.pending in order to check the status of the operation.

Now, the code (I'm retrieving subcategories from the category in a shop):

        data(){
            return{
                docs: []
            }
        },

        async fetch() {
            try{
                const res = await this.$prismic.api.query(this.$prismic.predicates.at('my.category.uid', this.$route.params.sub))
                const el = res.results[0]
                const query = await this.$prismic.api.query([this.$prismic.predicates.at('document.type','sub-category'), this.$prismic.predicates.at('my.sub-category.category', el.id)], { orderings: '[my.sub-category.position, my.sub-category.title]' })
                this.docs = query.results
            }   
            catch (e) {
                console.log(e)
            }
        }

This works at least client side. Will this work for proper SSR? I mean, in asyncData() (which is invoked server side), this is not accessible, nor the data(). If this is the right solution, what's the deal for using asyncData() over fetch() anymore?

Upvotes: 0

Views: 8481

Answers (3)

tony19
tony19

Reputation: 138326

As far as SSR is concerned, fetch is used the same way asyncData is used (although there is a difference in page transition timing).

The difference between fetch and asyncData:

  • asyncData does not have access to this, while fetch (in Nuxt >= 2.12) does.
  • asyncData's return value sets the data of the component, while fetch's return value does not because fetch already has access to this, so it could set data props directly.
  • fetch is controlled by two options (fetchOnServer and fetchDelay), while asyncData has no options. When fetchOnServer (true by default) is set to false, the fetch hook is only called client-side.
  • fetch is coupled with the $fetchState object, which provides pending and timestamp (related to the current state of the fetch operation); and the error function (allows you to display an error message). Note that asyncData is also provided the error function in its context argument.
  • fetch allows for faster page transitions (according to RFC 27).
  • asyncData is only available to pages, while fetch can be used by any component (including pages).

Aside from the differences above, the semantics of the two methods are identical.

It seems fetch is unofficially the successor of asyncData even though both exist simultaneously without a deprecation notice. Since fetch provides more functionality and addresses the problems of asyncData, I would reccomend using fetch exclusively.

Upvotes: 7

sintj
sintj

Reputation: 814

RESOLVED: Nuxt prompted me with a tiny warning saying that "node-fetch" at least at version 2.6.0 is required. For some reason mine was at 2.1.2.

Upvotes: 2

Eduard
Eduard

Reputation: 133

If you are looking for a way to have a loading state for your page components you could use the data with asyncData,

        data(){
            return{
                loading: true
            }
        },

        async asyncData(context) {
            try{
                const res = await context.app.$prismic.api.query(context.app.$prismic.predicates.at('my.category.uid', context.app.$route.params.sub))
                const el = res.results[0]
                const query = await context.app.$prismic.api.query([context.app.$prismic.predicates.at('document.type','sub-category'), context.app.$prismic.predicates.at('my.sub-category.category', el.id)], { orderings: '[my.sub-category.position, my.sub-category.title]' })
                const docs = query.results
                const loading = false
                return {docs, el, loading}
            }   
            catch (e) {
                console.log(e)
            }
        }

if you use prismic, (and if you didn't register it as a plugin) you could inject prismic into the context through a plugin

import prismic from 'prismic-javascript'

export default (inject) => {

    inject('prismic', prismic)
}

So now you have access to $prismic in the context.

Another approach that I am using when dealing with loading states is using a store module for handling the state of the request.

plugins/loading.js

export default ({store},inject) => {
    inject('changeLoading', function(){
            store.dispatch('changeLoading')
        }
    )
}

store/index.js

export const state = () => ({
    loading: false
});

export const mutations = {
    CHANGE_LOADING(state){
        state.loading = !state.loading
    }
}

export const actions = {
    changeLoading({commit}){
        commit('CHANGE_LOADING')
    }
}

export const getters = {
    getLoading(state){
        return state.loading
    }
}

so now instead of having a local loading state inside your components you would have a centralized state inside your store that you can change and get.

        data(){
            return{
                loading: this.$store.getters['getLoading']
            }
        },

        async asyncData(context) {
            try{
                context.app.$changeLoading()
                const prismicData = await context.app.$prismic(...)
                context.app.$changeLoading()
                return {prismicData}
            }   
            catch (e) {
                context.app.$changeLoading()
                console.log(e)
            }
        }

Upvotes: 2

Related Questions