nuxt-community / composition-api

Composition API hooks for Nuxt 2.
https://composition-api.nuxtjs.org
MIT License
709 stars 101 forks source link

fix: useFetch in renderless component leads to error "Cannot convert undefined or null to object" #600

Open thekonz opened 2 years ago

thekonz commented 2 years ago

🐛 The bug I have a renderless component that uses useFetch. In ssr: true mode, it crashes the app with this error: Cannot convert undefined or null to object in serverPrefetch

node_modules/@nuxtjs/composition-api/dist/runtime/index.mjs

     : vm._fetchKey || vm.$ssrContext.fetchCounters['']++
  if (!vm.$vnode.data) vm.$vnode.data = {}
  const attrs = (vm.$vnode.data.attrs = vm.$vnode.data.attrs || {})
  attrs['data-fetch-key'] = vm._fetchKey
  const data = { ...vm._data }
  Object.entries(vm.__composition_api_state__.rawBindings).forEach(
    ([key, val]) => {
      if (val instanceof Function || val instanceof Promise) return
      data[key] = isRef(val) ? val.value : val
    }
  )

The issue is that vm.__composition_api_state__.rawBindings is not set for a renderless component.

🛠️ To reproduce Steps to reproduce the behavior:

  1. Download the repo https://github.com/thekonz/reproduce-bug-nuxt-composition-use-fetch
  2. Run yarn dev
  3. See that there is no error, if you set ssr: false in nuxt.config.js

Repo for reproduction: https://github.com/thekonz/reproduce-bug-nuxt-composition-use-fetch Component: https://github.com/thekonz/reproduce-bug-nuxt-composition-use-fetch/blob/master/components/outer.vue

🌈 Expected behaviour

I expect it to work the same on ssr as in client.

thekonz commented 2 years ago

I wrote the same with bridge in this branch https://github.com/thekonz/reproduce-bug-nuxt-composition-use-fetch/tree/bridge It works, but using bridge would be quite a bit of work in our project, so I hope we can get a fix or a recommendation here.

Another workaround is to make the component render a div with a slot and pass props to the slot without using () => slots.default(), but then the component returns one root node instead of many.

Chapaev17 commented 2 years ago

Same error Cannot convert undefined or null to object when using useFetch, when setup function returns tsx. When using the render function, everything works fine.

maninak commented 2 years ago

@Chapaev17 that's weird that you only get this with JSX, because I came to find this ticket after getting this error, even though I'm using render functions a.k.a. h().

For reference here's my code

// useCmsifiedPage.ts

import type { Ref } from '@nuxtjs/composition-api'
import { usePageMeta } from '~/composables'
import type { Page } from '~/types'
import { getSections } from '~/utils'

type MetaToAppend = Parameters<typeof usePageMeta>['1']

interface Props {
  slug?: string
  metaToAppend?: MetaToAppend
}

export function useCmsifiedPage(strapiResponse: Ref<Page[] | null>, props: Props = {}) {
  const page = computed(() => strapiResponse.value?.[0])
  const sections = computed(() => getSections(page?.value))

  function renderPage() {
    return h('div', { class: 'mt-6' }, [
      Object.entries(sections.value).map(([componentName, componentProps], index) =>
        h(`lazy-${componentName}`, {
          props: componentProps,
          key: `${componentName}-${index}`,
          class: 'mb-6',
        }),
      ),
    ])
  }

  usePageMeta(page, props.metaToAppend)

  return { page, sections, renderPage }
}