简体   繁体   中英

How to orchestrate async dependencies with Suspense Component and Vuex in Vue 3?

I'm trying to use Vue 3 Suspense component for my APP but all of the examples that I have found load the asynchronous data within the component, in my case I'm getting all the data from a WebSocket and then use a Vuex module to inject that data in my APP via a getter.

Basically, the WebSocket sends chats, then I store those chats in an array using actions and mutations from the Vuex module and then I use a getter to expose that array of chats to my APP.

This is the vuex module that deals with the WebSocket, I'm just adding the code to fetch chats as soon as the Websocket sends a message from the backend. As you can see I'm using a getter in order to expose the incomingChats array to my APP.

export const state = {
  connected: false,
  error: null,
  connectionId: "",
  incomingChats: [],
  socket: {},
};
export const actions = {
  async processWebsocket({ dispatch, rootState, commit }) {
    const socket = await new WebSocket(settings.MY_WEBSOCKET);
    socket.onmessage = function (event) {
      const socketData = JSON.parse(event.data);
      const socketDataType = socketData.type;
      if (
        socketData.connectionId &&
        socketData.connectionId !== state.connectionId
      ) {
        commit("SET_CONNECTION_ID", socketData.connectionId);
        dispatch("shifts/updateEventsSubscription", rootState.token.agentId, {
          root: true,
        });
      } else {
        switch (socketDataType) {
          case "incoming-chats-updated":
            dispatch("setIncomingChats", socketData.incomingChats);
            break;
          }
        }
      }
    };
  },
  async setIncomingChats({ commit }, incomingChats) {
    commit("SET_INCOMING_CHATS", incomingChats);
  },
};
export const mutations = {
  SET_INCOMING_CHATS(state, incomingChats) {
    state.incomingChats = incomingChats;
  },
};
export const getters = {
  getIncomingChats: (state) => {
    return state.incomingChats;
  },
};

Then this is the part where I'm trying to use Suspense. Basically a parent component (A Chat Queue) that will paint the chats (BaseChat component) that are coming from the websocket:

<template>
  <div>
    <Suspense>
      <template #default>
        <ul class="overflow-y-auto pr-2">
          <BaseChat
            v-for="(chat, index) in incomingChats"
            :key="index"
            :chat="chat"
            :class="{ 'mt-0': index === 0, 'mt-4': index > 0 }"
            @click="assigningChatToAgent(chat.id)"
          />
        </ul>
      </template>
      <template #fallback> Loading Article </template>
    </Suspense>
  </div>
</template>

<script>
import { useGetters, useActions } from "vuex-composition-helpers";
import BaseChat from "@/components/BaseChat.vue";
import ChatService from "@/services/ChatService.js";
export default {
  components: {
    BaseChat,
  },
  setup() {
    const { incomingChats, agentId } = useGetters({
      incomingChats: "websocket/getIncomingChats",
      agentId: "token/getAgentId",
    });
    const { addChatSession } = useActions({
      addChatSession: "chatSession/addChatSession",
    });
    function assigningChatToAgent(chatId) {
      const agentIdValue = JSON.parse(JSON.stringify(agentId.value));
      const assignChatObject = {
        aggregateId: chatId,
        agentId: agentIdValue.agentId,
      };
      ChatService.assignChatToAgent(assignChatObject);
    }
    return {
      incomingChats,
      agentId,
      addChatSession,
      assigningChatToAgent,
    };
  },
};
</script>   

I know that I have to create an async setup function and then await for a promise to resolve, but I just don't know how to do that since I'm supposed to wait for a getter coming from my vuex module to be resolved, and all of the examples that I found do that inside the parent component (they fetch the data from within the component).

Another particularity here is that I'm using "vuex-composition-helpers" in order to use getters and actions, don't know if that affects the way Suspense has to be implemented.

The ideal scenario here would be for the APP to show a loading message while the "incomingChats" getter is not resolved. Will really appreciate any help.

I created HOC that helped me reduce the boilerplate, you can use your async operations inside a setup function (don't forget to make setup async too), and then just wrap your component when produce an import:

const withSuspense = (
  Component,
  SuspenseComponent,
  skeletonProps
) => {
  return defineComponent({
    components: {
      SuspenseComponent
    },

    setup(): (arg) {
      return (instance) =>
        h(Suspense, null, {
          fallback: () => h(SuspenseComponent, skeletonProps),
          default: () =>
            h(Component, { ...instance.$attrs }, { ...instance.$slots })
        });
    }
  });
};

and then wrap it:

export default withSuspense(YourComponent, Skeleton, skeletonProps);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM