首頁  >  問答  >  主體

如何將 RTK 查詢與選擇器結合使用

我正在使用 Redux ToolkitRTK 查詢 建立一個項目,並嘗試從 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粉729198207P粉729198207321 天前664

全部回覆(1)我來回復

  • P粉779565855

    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 的第二個參數,而我展示的方法使用第二個參數來傳遞查詢參數(您的篩選器中的過濾器)。案例)。

    據我所知,到目前為止還沒有解決方案可以完美運行並涵蓋以下所有內容:

    • 使用實體規範化
    • 使用選擇器檢索資料
    • 使用參數化查詢

    另一種方法可能是建立一些選擇器工廠,這些選擇器工廠為查詢參數的特定組合動態建立基本選擇器。

    我曾經製作過這樣一個包裝器,可以在所有情況下使用。不幸的是,我無法共享它,因為它是一個私有包,但基本思想是改變參數,以便selectByIdselectAll (以及所有其他選擇器)都可以正常工作,透過將查詢參數作為第三個參數傳遞給選擇器,然後進一步重新包裝每個實體適配器選擇器:

    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
    

    我知道這聽起來很複雜,我幾乎沒有讓它工作,所以盡量避免朝這個方向走:)

    可以找到我一路上找到的一篇有用的文章這裡,他們還描述了一些在組件級別創建選擇器並記住它們的方法,但我還沒有全部嘗試過。看看吧,也許您會找到更簡單的方法來解決您的特定問題。

    回覆
    0
  • 取消回覆