Как объединить данные из запроса RTK

У меня есть постраничные данные с моего сервера, которые я хотел бы отображать в бесконечной прокрутке. Я знаю, что могу справиться с этим с помощью чистого Redux, но мне бы хотелось сделать это с помощью Redux Toolkit. Я пробовал читать https://github.com/reduxjs/redux-toolkit/discussions/1163?sort=new , но не понимаю, как люди это сделали. RTK merge кажется наиболее многообещающим вариантом https://redux-toolkit.js.org/rtk-query/api/createApi#merge но пока моя реализация просто перезаписывает существующие данные новой страницей, а не добавляет их.

Код согласно примеру merge в официальной документации:

            query: ({accountId, page}) => {
                console.info('accountId', accountId);
                return {
                    url: `/api/transactions/account/${accountId}`,
                    method: 'get',
                    params: {page}
                }
            },
            serializeQueryArgs: ({endpointName}) => {
                return endpointName
            },
            merge: (currentCache, newItems) => {
              currentCache.push(...newItems)
            },
            forceRefetch({ currentArg, previousArg}) {
                return currentArg !== previousArg;
            },
        }),

выдает мне ошибку TypeError: Spread syntax requires ...iterable[Symbol.iterator] to be a function для currentCache.push(...newItems)

Я заменил это на return [...currentCache, ...newItems], но это говорит мне об этом TypeError: currentCache is not iterable

Что мне попробовать дальше? Данные находятся в response.data.data, что может немного сбить с толку. Мне также нужен доступ к response.data.last_page, чтобы знать, когда прекратить получение данных.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
0
388
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я предполагаю, что вам нужно убедиться, что currentCache всегда является массивом при выполнении операции распространения. вы можете гарантировать, что currentCache, выполнив:

merge: (currentCache = [], newItems) => [...currentCache, ...newItems],

Кроме того, чтобы получить доступ к response.data.last_page и прекратить получение данных, когда это необходимо, вам может потребоваться настроить логику вне функции слияния. Вы можете проверить значение last_page в своем компоненте или там, где вы отправляете действие для получения данных, и условно остановить дальнейшую выборку на основе этого значения.

во второй части вы правы — transactions проходит с данными подкачки, и я визуализирую карту transactions.data. К сожалению, основная проблема currentCache is not iterable остаётся.

maganthro 22.04.2024 01:27

Вы пробовали обновить функцию слияния? Поскольку я предполагаю, что ошибка, с которой вы столкнулись, связана исключительно с типом currentCache, который получает функция, убедитесь, что это массив, если нет, попробуйте назначить пустой массив.

Parv 22.04.2024 13:22

Также я вижу, что вы не возвращаетесь из функции merge. Обычно вам необходимо явно использовать оператор return для получения значения из функции. Если вы не включите оператор return, функция неявно вернет return.

Parv 22.04.2024 13:30

Согласно документации Since this is wrapped with Immer, you may either mutate the currentCacheValue directly, or return a new value, but not both at once. я считаю, что исходный пример currentCache.push напрямую изменяет значение и поэтому не требует оператора возврата. Модификация, которую я пробовал, использует return. А еще объединяйтесь Will only be called if the existing currentCacheData is not undefined - on first response, the cache entry will just save the response data directly.

maganthro 22.04.2024 14:16
Ответ принят как подходящий

Это был вопрос проб и ошибок, поскольку я никогда не мог увидеть значение currentCache, но понял, что мне нужно добавить response.data.data в currentCache. В итоге сработало следующее.

currentCache.data.push(...newItems.data);

Мой окончательный код:

export const transactionsSlice = apiSlice.injectEndpoints({
    endpoints: build => ({
        getTransactions: build.query({
            query: ({accountId, page}) => {
                return {
                    url: `/api/transactions/account/${accountId}`,
                    method: 'get',
                    params: {page}
                }
            },
            serializeQueryArgs: ({endpointName}) => {
                return endpointName
            },
            merge: (currentCache, newItems, { arg }) => {
                const { page } = arg;

                // this check deletes all transactions when account changes
                if (page === 1) {
                    return newItems;
                }

                // more complex update to replace existing data if required
                const updatedData = [
                    ...currentCache.data.filter(existingItem => !newItems.data.some(newItem => newItem.trans_id === existingItem.trans_id)),
                    ...newItems.data,
                ];
                return {
                    data: updatedData,
                    current_page: newItems.current_page,
                    last_page: newItems.last_page,
                };
            },
            forceRefetch({ currentArg, previousArg}) {
                return currentArg !== previousArg;
            },
            providesTags: (result = [], error, arg) => [
                'Transaction',
                ...(result.data ? result.data.map(({ id }) => ({ type: 'Transaction', id })) : []),
            ],
        }),
        deleteTransaction: build.mutation({
            query: (transactionId) => {
                return {
                    url: `/api/transactions/${transactionId}/delete`,
                    method: 'patch',
                }
            },

            // update cache to reflect deleted transaction
            async onQueryStarted(transactionId, { dispatch, queryFulfilled}) {
                const patchResult = dispatch(
                    transactionsSlice.util.updateQueryData('getTransactions', transactionId, (draft) => {
                        const transactionToUpdate = draft.data.find(transaction => transaction.trans_id === transactionId);
                        transactionToUpdate.deleted = 1;
                    })
                )
                queryFulfilled.catch(patchResult.undo);
            },
            invalidatesTags: (result, error, arg) => [
                { type: 'Account' }
            ],
        }),
    })
})

Обратите внимание, что я включил предоставление и аннулирование тегов, потому что в моем проекте существует некоторое взаимодействие между Account и Transaction, но это не является основным для работы с нумерацией страниц с RTK.

Хотя это решило мою насущную проблему, я собираюсь вернуться к управлению данными самостоятельно в простом сокращении, потому что я не мог должным образом управлять изменениями. Если я обновлю элемент с самой последней страницы, то ForceRefetch обновит эту страницу, но если я обновлю элемент с более ранней страницы, мне так и не удастся обновить соответствующую страницу.

maganthro 25.04.2024 14:47

Дальнейшее обновление: после небольшого исследования я обнаружил, что вы можете вручную обновить кэшированные данные, чтобы они соответствовали обновлениям сервера, без необходимости повторной загрузки данных. redux-toolkit.js.org/rtk-query/usage/manual-cache-updates Это идеально подошло для моих нужд. Я обновлю свой ответ полным кодом.

maganthro 25.04.2024 23:50

Другие вопросы по теме