我正在使用 Redux Toolkit 和 RTK 查詢 建立一個項目,並嘗試從 API 取得一些條目。我正在使用 createEntityAdapter 的規範化資料方法,因為在某個元件中我需要將資料作為數組,所以我最終使用了選擇器。現在我的問題是,由於我添加過濾器作為查詢的參數,我的選擇器停止工作。
我在這裡研究了類似的問題,例如:“如何使用帶有參數的 RTK 查詢選擇器?”,但我太笨了,無法理解我應該修改的內容。我試圖理解 RTK 查詢文檔,但我做不到。
從上面的問題中,我知道我的選擇器還需要具有參數,以便準確地知道要選擇什麼,並且這不是推薦的模式,但我無法理解如何使其工作。
我的條目切片:
import { createSelector, createEntityAdapter } from '@reduxjs/toolkit' import { apiSlice } from './apiSlice' const entryAdapter = createEntityAdapter() const initialState = entryAdapter.getInitialState({ ids: [], entities: {}, }) export const entryApiSlice = apiSlice.injectEndpoints({ endpoints: (builder) => ({ initialState, getEntry: builder.query({ query: (filters) => ({ url: '/history', params: filters, }), transformResponse: (responseData) => { return entryAdapter.setAll(initialState, responseData) }, providesTags: (result, error, arg) => [ { type: 'Entry', id: 'LIST' }, ...result.ids.map((id) => ({ type: 'Entry', id })), ], }), addEntry: builder.mutation({ query: (data) => ({ url: '/history/new', method: 'POST', body: data, }), invalidatesTags: [{ type: 'Entry', id: 'LIST' }], }), updateEntry: builder.mutation({ query: (initialEntry) => ({ url: `/history/${initialEntry.Id}`, method: 'PUT', body: { ...initialEntry, date: new Date().toISOString(), }, }), invalidatesTags: (result, error, arg) => [{ type: 'Entry', id: arg.id }], }), deleteEntry: builder.mutation({ query: ({ id }) => ({ url: `/history/${id}`, method: 'DELETE', body: { id }, }), invalidatesTags: (result, error, arg) => [{ type: 'Entry', id: arg.id }], }), }), }) export const { useGetEntryQuery, useAddEntryMutation, useUpdateEntryMutation, useDeleteEntryMutation, } = entryApiSlice export const selectEntryResult = (state, params) => entryApiSlice.endpoints.getEntry.select(params)(state).data const entrySelectors = entryAdapter.getSelectors( (state) => selectEntryResult(state) ?? initialState ) export const selectEntry = entrySelectors.selectAll
我在像這樣的 Entries 元件中使用它
const { data: entriesData = [], refetch, isLoading, isSuccess, isError, error, } = useGetEntryQuery(filters) const entries = useSelector(selectEntry)
注意:如果我從獲取查詢中刪除“過濾器”,一切都會像以前一樣工作(當然,正如預期的那樣)。
免責聲明:我不知道我到底在做什麼,我已經閱讀了文件並且正在嘗試弄清楚,所以我非常感謝任何反饋。 謝謝!
P粉7795658552023-12-29 00:13:52
是的,這是一個有點敏感的話題,因為 RTKQ 的文件傾向於顯示最簡單的範例,即完全不使用任何參數的查詢。我自己也遇到過很多問題。
無論如何,您已將 selectEntryResult
宣告為兩個參數的函數:state 和 params。然後,當您在其下方建立適配器選擇器時,您僅使用一個參數來呼叫它:狀態。此外,當您在元件中使用最終選擇器時,如下所示:
const entries = useSelector(selectEntry);
參數無處可尋,預設情況下它們未定義
,無法找到與此類查詢參數關聯的任何資料。
這裡要理解的關鍵是,您實際上需要以某種方式透過選擇器的每個層級(每個包裝器)傳遞查詢參數。
這裡的一種方法是透過選擇器「轉發」參數:
export const selectEntryResult = createSelector([state => state, (_, params) => params], (state, params) => entryApiSlice.endpoints.getEntry.select(params)(state)?.data ?? initialState)
這裡我們使用從RTK匯出的createSelector
函數。然後在你的元件中你會做這樣的事情:
const {...} = useGetEntryQuery(filters); const entries = useSelector(state => selectEntry(state, filters));
這在使用實體適配器建立的 selectAll
選擇器時有效,但在使用 selectById
時會導致問題,因為該選擇器也是參數化的。簡而言之,selectById
選擇器在內部定義為使用您希望檢索的實體id 的第二個參數,而我展示的方法使用第二個參數來傳遞查詢參數(您的篩選器中的過濾器)。案例)。
據我所知,到目前為止還沒有解決方案可以完美運行並涵蓋以下所有內容:
另一種方法可能是建立一些選擇器工廠,這些選擇器工廠為查詢參數的特定組合動態建立基本選擇器。
我曾經製作過這樣一個包裝器,可以在所有情況下使用。不幸的是,我無法共享它,因為它是一個私有包,但基本思想是改變參數,以便selectById
和selectAll
(以及所有其他選擇器)都可以正常工作,透過將查詢參數作為第三個參數傳遞給選擇器,然後進一步重新包裝每個實體適配器選擇器:
export const createBaseSelector = (endpoint) => createSelector( [(state) => state, (_, other) => other, (_, _other, params) => params], (state, _other, params) => endpoint.select(params)(state) ); const selectors = adapter.getSelectors(baseSelector); // And then rewrap selectAll and selectById from 'selectors' above
我知道這聽起來很複雜,我幾乎沒有讓它工作,所以盡量避免朝這個方向走:)
可以找到我一路上找到的一篇有用的文章這裡,他們還描述了一些在組件級別創建選擇器並記住它們的方法,但我還沒有全部嘗試過。看看吧,也許您會找到更簡單的方法來解決您的特定問題。