Daniel
Daniel

Reputation: 8677

How to prevent execution of useFetch in nuxt3 when body was changed

Scenario:

  1. There is login page.
  2. We want to send request to server ONLY when login button is pushed.
  3. We do not want to send request when user changed input.

This is my code:

const body = ref<{ identifier: string, password: string }>({
  identifier: '',
  password: ''
});

const {data, execute, error, pending} = await useFetch(`${config.public.baseUrl}/api/auth/local`, {
  body,
  lazy: true,
  method: 'post',
  immediate: false,
  watch: [],
})

async function login() {
  console.log("login");
  await execute();
}

and template

    <form @submit.prevent="login">
      <label for="email">
        Email
        <input type="text" v-model="body.identifier">
      </label>
      <label for="password">
        Password
        <input type="password" v-model="body.password">
      </label>

      <button>Login</button>
    </form>

unfortunately even if I do not click button this form will send post request to /api/auth/local every time when user type letter in this form.

This behavior is described in documentation:

https://nuxt.com/docs/api/composables/use-fetch

All fetch options can be given a computed or ref value. These will be watched and new requests made automatically with any new values if they are updated.

I need to override this feature.

Change of

v-model

to

v-model.lazy

helps a little bit, but I still can't control exact time when this request is send.


My current workaround

const body = ref<{ identifier: string, password: string }>({
  identifier: '[email protected]',
  password: ''
});

const loginBody = ref<{ identifier: string, password: string }>({
  identifier: '',
  password: ''
});

const {data, execute, error, pending} = await useFetch(`${config.public.baseUrl}/api/auth/local`, {
  body: loginBody,
  lazy: true,
  method: 'post',
  immediate: false,
  watch: [],
})

async function login() {
  console.log("login");
  loginBody.value = body.value;
  await execute();
}

is not enough good, because it sends actually 2 requests in the same time, but first is immediately cancelled.

Upvotes: 2

Views: 4528

Answers (4)

Mick
Mick

Reputation: 8922

Your body is a ref() and therefor your useFetch() will watch it and execute again when it changes. You have two options:

  1. Don't use ref() for your body. Does it really need to be a ref? Is it used in <template>? If you dont need a variable in your template, or if you use it in your template but it never changes then it does not need to be a ref()
  2. In your useFetch() set watch: false so it will not execute when your body ref() changes

Upvotes: 0

meir
meir

Reputation: 106

You need to set in the options watch: false

See more here

Upvotes: 2

vampirefan
vampirefan

Reputation: 71

I meet the same situation. You could just unref the option value for useFetch() by using {...loginbody}.

const { data } = await useFetch('/login', { method: 'post', body: {...loginbody} })

Upvotes: 7

Daniel
Daniel

Reputation: 8677

Finally i achieved my goal writing function

async function login() {
  if(JSON.stringify(loginBody.value) === JSON.stringify(body.value)) {
    await execute();
  } else {
    loginBody.value = body.value;
  }
}

Request is executed once always when login function is called. It body changed his value, then assign to loginBody triggers request. In other case we executing is manually by execute.

Little over-engineered but works.

Upvotes: 0

Related Questions