简体   繁体   中英

VueJS 3: update value and avoid watch

I am coding a table with pagination component, and I use multiple v-model and watch on these variables to fetch the data. When perPage is updated, I want to reset page to 1. So I did it in my watch method but of course, watch is triggered twice (for perPage and then page ).

Is it possible to update the variable and disable watch at this moment?

Here is my current code:


<script setup lang="ts">

const sort = ref(route.query.sort || 'created_at')
const filters = ref(route.query.filters || {})
const page = ref(route.query.page ? parseInt(route.query.page.toString()) : 1)
const perPage = ref(route.query.per_page ? parseInt(route.query.per_page.toString()) : 10)

watch([sort, filters, page, perPage], ([oldSort, oldFilters, oldPage, oldPerPage], [newSort, newFilters, newPage, newPerPage]) => {
  if (oldPerPage !== newPerPage)
    page.value = 1

  fetchItems()

  router.push({
    query: {
      ...route.query,
      sort: sort.value,
      // filters: filters.value,
      page: page.value,
      per_page: perPage.value,
    },
  })
})

async function fetchItems() {
  items.value = await userApi.list({
    filters: toRaw(filters.value),
    sort: sort.value,
    page: page.value,
    perPage: perPage.value,
  })
}
</script>

<template>
    <CTable
      :pagination-enabled="true"
      v-model:sort="sort"
      v-model:page="page"
      v-model:per-page="perPage"
      :total-items="items.meta.total"
      :total-pages="items.meta.last_page"
    />
</template>

The only workaround I found is to return when I reset page :

watch(..., () => {
  if (oldPerPage !== newPerPage) {
    page.value = 1
    return
  }

  fetchItems()

  ...
})

It is working in my case but for some another cases I would like to update without trigger the watch method.

Thanks!

Create another watcher for perPage :

watch([sort, filters, page, perPage], ([oldSort, oldFilters, oldPage, oldPerPage], [newSort, newFilters, newPage, newPerPage]) => {

  fetchItems()

  router.push({
    query: {
      ...route.query,
      sort: sort.value,
      // filters: filters.value,
      page: page.value,
      per_page: perPage.value,
    },
  })
})

watch(perPage, (newPerPage,oldPerPage ) => {
  if (oldPerPage !== newPerPage)
    page.value = 1
})

It's recommended to create watch for a single property separately to avoid unnecessarily updates and conflicts. For the first watch try to replace it with watchEffect like since you're not using the old/new value:

watchEffect(() => {
  fetchItems()
  router.push({
    query: {
      ...route.query,
      sort: sort.value,
      // filters: filters.value,
      page: page.value,
      per_page: perPage.value,
    },
  })
})

Considering that the state is changed through v-model , it needs to be observed with a watcher.

In order to avoid a watcher to be triggered multiple times, it should implement additional conditions like shown in the question, but it also needs to not skip an update when page is already 1 , this won't result in additional update:

  if (oldPerPage !== newPerPage) {
    page.value = 1

    if (newPage !== 1)
      return
  }

Otherwise there need to be multiple watchers, like another answer suggests. In case a watcher that causes asynchronous side effects ( fetchItems ) shouldn't be triggered more than it needs, and there are other causes for this to happen besides perPage , it can be debounced, eg:

watch(perPage, () => {
  page.value = 1
});

watch([sort, filters, page, perPage], debounce(() => {
  fetchItems()
  ...
}, 100));

Thank you for all your answers, I finally found what I was looking for using VueUse - watchIgnorable :

const { stop, ignoreUpdates } = watchIgnorable(page, (value) => {
  fetchItems()
})

watch(perPage, (newPerPage, oldPerPage) => {
  if (newPerPage !== oldPerPage) {
    ignoreUpdates(() => {
      page.value = 1
    })
  }

  fetchItems()
})

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