Implementing a loading spinner in sveltekit that is triggered during an action

2.8k Views Asked by At

I have a simple form having email, password and confirmPassword. I use action to handle this. I want to implement a spinner that will be triggered for the following actions

  1. Checking if user is currently existing in the db
  2. If no, then proceed for registration

I am using pocketbase

Following is my action.

import { superValidate } from 'sveltekit-superforms/server';
import { redirect } from '@sveltejs/kit';
import { fail } from '@sveltejs/kit';

import { loginSchema } from '$lib/schema/zodschema';
import { ClientResponseError } from 'pocketbase';

export const load = async () => {
    const form = await superValidate(loginSchema);
    return { form };
};

export const actions = {
    default: async ({ locals, request }) => {
        const form = await superValidate(request, loginSchema);


        try {

            const { email } = form.data
            const records = await locals.pb.collection('test').getFullList();
            const userRecords = records.filter(value => value.email === form.data.email);

            if (userRecords.length > 0) {

                const existingUser = userRecords[0]

                if (existingUser && existingUser.verified) {
                    return {

                        accountCreated: false,
                        message: 'The user records exists. Proceed to login instead',
                        isVerified: true,

                    }
                } else {


                    return {

                        accountCreated: false,
                        message: 'The user record exists. You have to verify to access',
                        isVerified: false,


                    }
                }
            } else {
                await locals.pb.collection('test').create(form.data);



                return {

                    accountCreated: true,
                    message: 'The user record is successfully created',
                    isVerified: false,
                }
            }



        } catch (error) {
            // Handle the error

            if (error instanceof ClientResponseError) {
                return {
                    error: error.message,
                    isLoading: false
                }
            }
        }
    }
};

In the above, I could set a boolean like

let isLoading = true

Then set it to false at different stages. But the problem is how to access the isLoading status in the client (both initial and updated state).

I tried stores only to find out later that stores cannot be used to share the state between the client and server.

Is there an alternative approach to achieve this?

Thanks

3

There are 3 best solutions below

1
On BEST ANSWER

It is not entirely clear to me, but it seems that you are trying to use Sveltekit form actions. Also, it is not clear to me why the loader needs to change during the server load.

What I would recommend is that you use a loading boolean in the client when the request starts and ends using progressive enhancement:

<script>
    import { enhance } from '$app/forms';

    /** @type {import('./$types').PageData} */
    export let data;

    let formLoading = false;
</script>

...
{#if formLoading}
    Loading...
{/if}
...
<form action="?/..." method="post" use:enhance={() => {
    formLoading = true;
    return async ({ update }) => {
        formLoading = false;
        update();
    };
}}>
    <button>SUBMIT</button>
</form>

Relative documentation: https://kit.svelte.dev/docs/form-actions#progressive-enhancement

2
On

Have you considered using the {#await} block?
What you can do is when you fetch the database it triggers the await block and when it's done, you can decide from there if you want to continue login or send to the registration page.

Refer to: https://svelte.dev/tutorial/await-blocks

So it can looks something like:

<script>
  let data;
  async function formSubmit() {
    data = await fetchData();

    ...
  }
</script>

{#await data}
  <Spinner />
{:then receivedData}
  <!-- Decide to send to registration or not -->
  <!-- you can either use the recievedData or just omit it -->
{:catch}
 <!-- Handle error -->
{/await}
0
On

I had to reorder the first answer like this:

  const formSubmit: SubmitFunction = ({ form, data, action, cancel }) => {
    formLoading = true;
    return async ({ update }) => {
      await update(); // Wait for this to complete
      formLoading = false;
    };
  };