Problem in doing paginations between 2 composables

Hi, so I am making 2 composables usePaginations.ts
export const usePaginations = () => {
const perPage = ref<number>(8)
const totalItems = ref<number>(0)

const currentPage = ref(0)
const cursorNextPage = ref<string | null>(null)
const cursorPrevPage = ref<string | null>(null)
const cursor = ref<string | null>(null)
const hasNextPage = ref<boolean>(false)
const hasPreviousPage = ref<boolean>(false)
const isForward = ref(true)
const totalCount = ref(0)

const updateFromPaginator = (e: any) => {
e.page > currentPage.value ? nextPage() : prevPage()
}

// Go to the next page using the new cursor
const nextPage = (): void => {
isForward.value = true
if (hasNextPage.value && cursorNextPage.value) {
cursor.value = cursorNextPage.value
currentPage.value++
}
}

// Go to the previous page by removing the last cursor and updating the current one
const prevPage = (): void => {
isForward.value = false
if (hasPreviousPage.value && cursorPrevPage.value) {
nextTick(() => {
cursor.value = cursorPrevPage.value
currentPage.value--
})
}
}

// Reset pagination (clear cursor history)
const resetPagination = (): void => {
cursor.value = null
}

return {
perPage,
totalItems,
cursor,
cursorNextPage,
cursorPrevPage,
hasNextPage,
hasPreviousPage,
resetPagination,
updateFromPaginator,
isForward,
totalCount
}
}
export const usePaginations = () => {
const perPage = ref<number>(8)
const totalItems = ref<number>(0)

const currentPage = ref(0)
const cursorNextPage = ref<string | null>(null)
const cursorPrevPage = ref<string | null>(null)
const cursor = ref<string | null>(null)
const hasNextPage = ref<boolean>(false)
const hasPreviousPage = ref<boolean>(false)
const isForward = ref(true)
const totalCount = ref(0)

const updateFromPaginator = (e: any) => {
e.page > currentPage.value ? nextPage() : prevPage()
}

// Go to the next page using the new cursor
const nextPage = (): void => {
isForward.value = true
if (hasNextPage.value && cursorNextPage.value) {
cursor.value = cursorNextPage.value
currentPage.value++
}
}

// Go to the previous page by removing the last cursor and updating the current one
const prevPage = (): void => {
isForward.value = false
if (hasPreviousPage.value && cursorPrevPage.value) {
nextTick(() => {
cursor.value = cursorPrevPage.value
currentPage.value--
})
}
}

// Reset pagination (clear cursor history)
const resetPagination = (): void => {
cursor.value = null
}

return {
perPage,
totalItems,
cursor,
cursorNextPage,
cursorPrevPage,
hasNextPage,
hasPreviousPage,
resetPagination,
updateFromPaginator,
isForward,
totalCount
}
}
` So the problem is that the computed variables has logger and it logged with correct variables object but actually the query passed empty object and this is only happens when prevPage is invoked. nextPage works beautifully. Helps, appreciated. Thank you.
3 Replies
kapa.ai
kapa.ai2w ago
Hi @Mandarin Duck. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Based on the code you've shared, I can see you're implementing a cursor-based pagination system in Nuxt using composables. The issue you're experiencing where prevPage() doesn't work correctly while nextPage() does is likely related to the timing of state updates. The Problem Looking at your code, I notice a key difference between your nextPage() and prevPage() implementations:
// nextPage() updates cursor.value directly
const nextPage = (): void => {
isForward.value = true
if (hasNextPage.value && cursorNextPage.value) {
cursor.value = cursorNextPage.value
currentPage.value++
}
}

// prevPage() wraps the cursor update in nextTick()
const prevPage = (): void => {
isForward.value = false
if (hasPreviousPage.value && cursorPrevPage.value) {
nextTick(() => {
cursor.value = cursorPrevPage.value
currentPage.value--
})
}
}
// nextPage() updates cursor.value directly
const nextPage = (): void => {
isForward.value = true
if (hasNextPage.value && cursorNextPage.value) {
cursor.value = cursorNextPage.value
currentPage.value++
}
}

// prevPage() wraps the cursor update in nextTick()
const prevPage = (): void => {
isForward.value = false
if (hasPreviousPage.value && cursorPrevPage.value) {
nextTick(() => {
cursor.value = cursorPrevPage.value
currentPage.value--
})
}
}
The issue is likely that you're using nextTick() in the prevPage() method, which defers the cursor update to the next DOM update cycle. This means that when your query is being constructed, it's using the old (empty) cursor value. Solution Try removing the nextTick() wrapper in your prevPage() method to make it consistent with nextPage():
const prevPage = (): void => {
isForward.value = false
if (hasPreviousPage.value && cursorPrevPage.value) {
cursor.value = cursorPrevPage.value
currentPage.value--
}
}
const prevPage = (): void => {
isForward.value = false
if (hasPreviousPage.value && cursorPrevPage.value) {
cursor.value = cursorPrevPage.value
currentPage.value--
}
}
If you need to ensure reactivity when working with multiple composables, you might want to look at using toRefs() when passing props between components and composables, as shown in this Stack Overflow solution: How to pass a reactive object with prop values from a component to a composable in vue3 / nuxt3. Alternative Approach If you're building a pagination system, you might also consider using Nuxt UI's pagination component which handles a lot of these edge cases for you: Nuxt UI Pagination Component. For data fetching with pagination, the useLazyAsyncData composable can be helpful as it doesn't block navigation while data is being fetched: useLazyAsyncData Documentation. Let me know if this resolves your issue or if you need further assistance! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@482905063505920022> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mandarin Duck
Mandarin DuckOP2w ago
useProducts.ts
import { GET_PRODUCTS } from '../gql/queries/GetProducts'
import { GET_PRODUCTS_BY_COLLECTION_IDS } from '../gql/queries/GetProductsByCollectionIds'

export const useProducts = (pagination) => {
const products = ref([])
const categoryId = ref<string>('')
const collectionId = ref<string>('')

const variables = computed(() => {
const result = pagination.isForward.value
? {
first: pagination.perPage.value,
after: pagination.cursor.value
}
: {
last: pagination.perPage.value,
before: pagination.cursor.value
}

console.log('Variables computed:', {
isForward: pagination.isForward.value,
cursor: pagination.cursor.value,
result
})

return result
})

const {
result: dataProducts,
error,
refetch
} = useQuery(GET_PRODUCTS, () => variables.value, {
fetchPolicy: 'network-only',
enabled: computed(
() => !!pagination.cursor.value || pagination.isForward.value
)
})

watchEffect(() => {
// console.log('Current variables:', variables.value)
// console.log('Query result variables:', dataProducts.value)

if (dataProducts?.value?.products?.edges) {
const edges = dataProducts.value.products.edges
products.value = edges

const pageInfo = dataProducts.value.products.pageInfo
// console.log(':duck: ~ watchEffect ~ pageInfo:', pageInfo)
const totalCount = dataProducts.value.products?.totalCount
pagination.cursorNextPage.value = pageInfo.endCursor
pagination.cursorPrevPage.value = pageInfo.startCursor
pagination.hasNextPage.value = pageInfo.hasNextPage
pagination.hasPreviousPage.value = pageInfo.hasPreviousPage
pagination.totalCount.value = totalCount
}
})

return {
data: products,
categoryId,
collectionId,
refetch,
resetFilters,
dataByCollectionIds: dataProductsByCollectionIds
}
}
import { GET_PRODUCTS } from '../gql/queries/GetProducts'
import { GET_PRODUCTS_BY_COLLECTION_IDS } from '../gql/queries/GetProductsByCollectionIds'

export const useProducts = (pagination) => {
const products = ref([])
const categoryId = ref<string>('')
const collectionId = ref<string>('')

const variables = computed(() => {
const result = pagination.isForward.value
? {
first: pagination.perPage.value,
after: pagination.cursor.value
}
: {
last: pagination.perPage.value,
before: pagination.cursor.value
}

console.log('Variables computed:', {
isForward: pagination.isForward.value,
cursor: pagination.cursor.value,
result
})

return result
})

const {
result: dataProducts,
error,
refetch
} = useQuery(GET_PRODUCTS, () => variables.value, {
fetchPolicy: 'network-only',
enabled: computed(
() => !!pagination.cursor.value || pagination.isForward.value
)
})

watchEffect(() => {
// console.log('Current variables:', variables.value)
// console.log('Query result variables:', dataProducts.value)

if (dataProducts?.value?.products?.edges) {
const edges = dataProducts.value.products.edges
products.value = edges

const pageInfo = dataProducts.value.products.pageInfo
// console.log(':duck: ~ watchEffect ~ pageInfo:', pageInfo)
const totalCount = dataProducts.value.products?.totalCount
pagination.cursorNextPage.value = pageInfo.endCursor
pagination.cursorPrevPage.value = pageInfo.startCursor
pagination.hasNextPage.value = pageInfo.hasNextPage
pagination.hasPreviousPage.value = pageInfo.hasPreviousPage
pagination.totalCount.value = totalCount
}
})

return {
data: products,
categoryId,
collectionId,
refetch,
resetFilters,
dataByCollectionIds: dataProductsByCollectionIds
}
}

Did you find this page helpful?