storyblok / storyblok-nuxt

Storyblok Nuxt module
https://www.storyblok.com/tp/nuxt-js-multilanguage-website-tutorial
MIT License
278 stars 43 forks source link

Streamlining useAsyncStoryblok to nuxt's useAsyncData #836

Open jrutila opened 6 months ago

jrutila commented 6 months ago

Description

In the latest versions useAsyncStoryblok returns only the story. You cannot control the useAsyncData that is inside the useAsyncStoryblok at all. For example, you would like to instruct lazy or client only fetching.

To be more in line with the way nuxt composables like useAsyncData or useFetch work the useAsyncStoryblok should return the object that contains the usual data, pending, etc properties. One could also provide the AsyncDataOptions for the composable.

This would be a breaking change and thus needs more discussion.

I can submit a PR for this if the idea sounds solid.

Suggested solution or improvement

This implementation of useAsyncStoryblok should work as described above.

import { useStoryblokApi, useStoryblokBridge } from "@storyblok/vue";
import type { ISbStoriesParams, StoryblokBridgeConfigV2, ISbStoryData } from '@storyblok/vue';
import { useState, onMounted, useAsyncData } from "#imports";
import { type AsyncDataOptions } from "#imports";

export const useAsyncStoryblok = async (
  url: string,
  apiOptions: ISbStoriesParams = {},
  bridgeOptions: StoryblokBridgeConfigV2 = {},
  asyncOptions: AsyncDataOptions = {}
) => {
  const storyblokApiInstance = useStoryblokApi();
  const uniqueKey = `${JSON.stringify(apiOptions)}${url}`;
  const story = useState<ISbStoryData>(`${uniqueKey}-state`);

  onMounted(() => {
    if (story.value && story.value.id) {
      useStoryblokBridge(
        story.value.id,
        evStory => (story.value = evStory),
        bridgeOptions,
      );
    }
  });

  return await useAsyncData(
    uniqueKey,
    () => {
      return storyblokApiInstance
        .get(`cdn/stories/${url}`, apiOptions)
        .then((r) => r.data.story)
    },
    asyncOptions
  )
};

This could be then called like this (from playground folder):

const { data: story, pending } = await useAsyncStoryblok(`vue/articles/${path.params.slug}`, {
  version: "draft",
  language: "en"
}, {},
{ server: false, lazy: true, dedupe: "defer" }); // <- any asyncData options to give the control to the dev

Additional context

No response

Validations

jrutila commented 6 months ago

Created the PR after all...

johnjenkins commented 5 months ago

thanks - need this too. Also the current implementation loses any error context from the storyblok fetch and doesn't throw any errors too

alvarosabu commented 4 months ago

Hi @jrutila thanks for reaching out! Can you confirm for me if the PR I linked is correct?

Since this introduces a breaking change let me discuss it internally with the team on how would be the best approach. 🙏

jrutila commented 4 months ago

Yes, the PR is correct. I implemented some deprecation warning already, but please do discuss it through and change as needed.