Updating the Nuxt3 editing page with useFetch and $fetch for fetching data, along with a typed storage object that prevents loading issues

My journey with nuxt3 is still new. I am currently working on developing a new API-based app with nuxt3. Previous versions seemed to work "somehow," but they did not meet my expectations. Here are the things I am looking to cover, both in theory and in code:

1. Create a typed "container" for storage that can be used to track the current state of the form.

# types/models.ts
export type Client = {
  id?: number; 
  name: string;
}

# The ID is optional as the container can be used for new records as well, which do not have an ID yet.

I have imported this in my view:

<script setup lang="ts">
  import { Client } from '~/types/models'

  const { auth } = useSupabaseAuthClient();
  const { data: { session } } = await auth.getSession()

  const route = useRoute();

  # I am struggling to assign the type to use it in the views successfully.
  const client = ref<Client>({
    name: "a",
  });

  # Since the result attributes are wrapped in "client" in the response, I have used the following interface
  interface TransformedData {
    client: Client
  }

  const { data:result } = await useFetch(`http://localhost:3000/clients/${route.params.id}`, {
    headers: {
      'Authorization': `Bearer ${session?.access_token}`
    },
    transform: (result) => (result as TransformedData).client,
  });

  # I am struggling to pass the result correctly to the client.
  client.value = await(result.value as Client);


  const submitForm = async () => {
    const { data:result } = await useFetch(`http://localhost:3000/clients/${route.params.id}`, {
      method: "PATCH",
      body: {
        client: {
          name: client.value.name,
        }
      },
      headers: {
        'Authorization': `Bearer ${session?.access_token}`
      },
      transform: (result) => (result as TransformedData).client,
    });

    // client.name = (result.value as Client).name;
  }
</script>

<template>
  <form @submit.prevent="submitForm">
    <input v-model="client.name" type="text" name="name" id="name" />
    <button type="submit">Save</button>
  </form>
</template>

Essentially, I am looking for best practices to achieve the following:

  • Establish a "store" in the view (apologies for incorrect wording)
  • Retrieve data from a GET request and populate the store
  • Display the form and other elements on the page using the store data
  • Send a PATCH request using the "store" to update the server and refresh the view

Since this serves as the foundation for all other views, I am eager to have a well-thought-out implementation that makes sense, as opposed to just "somehow unexpectedly working" as I previously experienced.

Things I have attempted

  • Wrapped the GET request in a const loadData and called it onMounted, but it did not work.
  • Used useAsyncData and $fetch, encountered the same issues.
  • Explored various syntax and approaches throughout the code, but none seemed to work.
  • Scoured the depths of the internet for solutions.

If anyone has a best practice to share or ideas on how to tackle this, I would greatly appreciate it!

Thank you in advance!

Answer №1

After reaching a satisfactory state with my project, I've summarized some key points to share with others. While this solution works well for my personal project, I acknowledge the need for refinement in a client-centric project by consolidating common logic into a composable/base store to avoid code duplication.

Here is the setup in the view:

<script setup lang="ts">
  import { useClientStore } from "@/stores/client";
  import { useAuthStore } from "@/stores/auth";

  const { auth } = useSupabaseAuthClient();
  const authStore = useAuthStore();
  const store = useClientStore();

  const route = useRoute();

  onMounted(() => {
    authStore.auth = auth;
    store.fetchRecord(route.params.id);
  });
</script>

This section is crucial in the view:

<template>
    <button @click="store.fetchRecord(route.params.id)">FETCH</button>
    <button @click="store.updateRecord">UPDATE</button>
    {{ store.name }}
</template>

Additionally, here is a concise representation of the store with the fetch method (same structure for other methods):

import { useAuthStore } from "@/stores/auth";

export const useClientStore = defineStore('clients', {
  state: (): Client => ({
    id: null,
    name: ""
  }),

  getters: {
    record_url: (state) => `http://localhost:3000/clients/${state.id}`
  },

  actions: {
    async fetchRecord(id: any) {
      const { data: { session } } = await useAuthStore().auth.getSession();
      const token = `Bearer ${session?.access_token}`;

      const url = `http://localhost:3000/clients/${id}`;

      try {
        await useFetch(url, {
          headers: {
            'Authorization': token
          },
          onResponse({ response }) {
            const client = response._data.client as Client;
            useClientStore().$patch((state) => {
              state.id = client.id;
              state.name = client.name;
              // additional attribute translation here
            })
          },
        });
      } catch (error) {
        console.log(error)
      }
    },
  },
})

interface Client {
  id: number | null
  name: string
}

if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useClientStore, import.meta.hot))
}

Furthermore, I will explore the following areas for enhancing the solution:

  • Optimizing the sharing/access of auth tokens
  • Improving URL management for seamless integration with NuxtLink and evaluating its relevance
  • Reassessing the onMounted approach for initial data loading and token storage
  • Seeking overall enhancements

My experience with Nuxt has been positive so far. I welcome any insights or suggestions for refining this solution. Thank you [Json Landbridge]

Answer №2

Implement a "store" concept in the view (perhaps using the wrong terminology)

One popular choice among developers for managing state and centralized data storage is Pinia.

Retrieve data from the store using a GET request

Upon receiving the response from the request, update the store with that data, which will then reflect changes throughout the application.

Display the form and title based on the data stored in the store

Similarly, by binding the store correctly, any updates to the store will automatically reflect in the displayed content.

Use a PATCH request to utilize the "store" to update the view by sending data to the server

If you are looking to retrieve data from the store and send it to the server, you can achieve this easily with "getters" in Pinia. It's important to note that updating the view does not necessarily require sending data to the server, as all operations can be handled within the front-end.

EDIT

Example using Pinia with useFetch:

// Inside the component
// Access the created store:
const store = useStore()
// Make an API call
await useFetch('/api/auth/login', {
  onResponse({ response }) {
    // Update the store with the response, using `updateData` function you define
    store.updateData(response.data);
  },
})

[comment]: The store can potentially encapsulate interactions with APIs or remote data sources, although this was not explicitly mentioned.

For more insights, you may refer to: https://blog.logrocket.com/consume-apis-vuex-pinia-axios/#consuming-apis-pinia-axios

Here, Axios is used within a Pinia action to make API calls and update the store with the response.

[comment]: Can you provide an example of transferring data into the store?

The approach for transferring data into the store depends on the specific context, with options including useFetch and Axios inside the Pinia Actions.

If the data is only relevant to a particular component, using useFetch suffices. Otherwise, utilizing Axios inside the Pinia Actions is recommended for broader data usage.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to pass a variable to a text constant in Angular?

In my constant file, I keep track of all global values. Here is the content of the file: module.exports = { PORT: process.env.PORT || 4000, SERVER: "http://localhost:4200", FAIL_RESULT: "NOK", SUCCESSFUL_RESULT: "OK ...

Angular 2 forms are displaying ngvalid when input fields are marked as nginvalid

The form displays ngvalid because I have included the code like this: <form novalidate class="pop-form" (submit)="signUp()" #regForm="ngForm"> <div class="group"> <input type="text" [(ngModel)]="signUpData.name" [ngMode ...

Utilize fixed values in type declaration

Strange Behavior of Typescript: export type MyType = 0 | 1 | 2; The above code snippet functions correctly. However, the following code snippet encounters an issue: export const ONE = 1; export const TWO = 2; export const THREE = 3; export type MyType = O ...

Using Mat-Error for Two Way Binding leads to frequent triggering of ngModelChange事件

I am working with a mat input field that has two-way data binding using ngModel, and I want to add validation using mat-error and formControl. <mat-form-field [formGroup]="myForm"> <input matInput formControlName="myFormName" autocomplete="off" ...

Issues with Imported Routes Not Functioning as Expected

I am currently working on implementing routing in my Angular 2 project. All the components are functioning properly, but I encounter an error when I include 'appRoutes' in the imports section of app.module.ts. An unexpected TypeError occurs: C ...

Tips on creating a unit test for validating errors with checkboxes in your code base

In a certain scenario, I need to display an error message when a user clicks on the Next button without agreeing to the terms. To achieve this, I am looking to write a unit test case using Jest and React Testing Library. How can I go about doing this? im ...

Oops! It appears that there is an issue with the 'value' property in Vue3

I encountered an issue while trying to reference a state variable through the store mechanism import { AppState } from '@/types' import { localStorage } from '@/utils/storage'; import { defineStore } from 'pinia'; import { get ...

Angular 6 offers a dynamic auto complete feature that enhances user

My Angular app has auto-complete functionality enabled. I am using ngFor to iterate over an array of objects and passing the index of the object array to a function for some operations. Below is the code snippet I have tried: template.html <mat-form ...

Setting up a new folder in the internal storage within a React Native Expo environment

In my React Native Expo project, I am utilizing two functions to store data in a JSON file and then save the file to internal storage. However, the code currently asks for permission to store inside a chosen folder, but does not create the "ProjectName" fo ...

Exploring Tailwind's flexibility with custom color schemes

I'm attempting to generate unique hex colors for my React/TypeScript application with Tailwind. Why isn't the background color updating based on the color variable value? Check out my code snippet below: import React, { useState } from &apo ...

Steps for confirming whether each element in the array includes the specified search string using Typescript and protractor

How can I verify if each element in an array contains a specific search string in Typescript/Protractor? The issue I faced was that the console statements were returning false because they searched for exact matches instead of the search string. Any sugg ...

What is the most effective way to structure a React function incorporating nested objects and mapping?

As a newcomer to Typescript, I am facing challenges in properly typing the following code snippet. I have experimented with Interfaces and individually typing properties as well, but it seems like I am only scratching the surface and encountering new typin ...

How should dynamic route pages be properly managed in NextJS?

Working on my first project using NextJS, I'm curious about the proper approach to managing dynamic routing. I've set up a http://localhost:3000/trips route that shows a page with a list of cards representing different "trips": https://i.stack. ...

TypeScript encountered an error with code TS2554, indicating that it was expecting 0 arguments but instead received 1 in an Ionic application

Hello everyone! I'm encountering an issue in my project involving a Type Script error TS2554: Expected 0 arguments, but got 1. This error is preventing me from being able to select other options for custom input pop up. In this forum post, I have shar ...

Efficiently process and handle the responses from Promise.all for every API call, then save the retrieved data

Currently, I am passing three API calls to Promise.all. Each API call requires a separate error handler and data storage in its own corresponding object. If I pass test4 to Promise.all, how can I automatically generate its own error and store the data in ...

Passing asynchronous data from method1 to method2 without impacting the functionality of the script responsible for fetching the asynchronous data in method1

When working with TypeScript, I encountered an issue while trying to invoke an external script called SPCalendarPro within a private method that asynchronously fetches data. The script is invoked in the following manner: private _getSPCalendarPro() { con ...

The selectors in NgRx store are failing to retrieve data from the main global store

As I delve into the world of ngrx, I find myself struggling to fully understand and implement it effectively within my application. Recently, I integrated ngrx version 8.3 into my project in hopes of organizing my state management efficiently. My goal is ...

What is the process for waiting on RxJS data and how should it be retrieved?

I am faced with a situation where I need to create a user through a function, but before proceeding with the creation process, I have to verify whether another user with the same userName is already present in the session: public createUser(form: FormGroup ...

Explore the functionality of Typescript unit testing in debug mode with the assistance of VSCode

I'm looking to debug my Typescript spec.ts file using vs-code. Typically, I run it from the terminal like this: npm run test:unit -- page.spec.ts But I want to add some console.log statements for debugging. Is there a way to do this in vs-code? When ...

The object in an Angular 11 REACTIVE FORM may be null

I am looking to incorporate a reactive form validation system in my application, and I want to display error messages based on the specific error. However, I am encountering an error that says: object is possibly 'null'. signup.component.html &l ...