So verwenden Sie RTK-Abfragen mit Selektoren
P粉729198207
P粉729198207 2023-12-28 11:46:38
0
1
827

Ich erstelle ein Projekt mit Redux Toolkit und RTK Query und versuche, einige Einträge von der API abzurufen. Ich verwende die Methode „Daten normalisieren“ von createEntityAdapter, weil ich in einer bestimmten Komponente die Daten als Array benötige, also habe ich letztendlich einen Selektor verwendet. Mein Problem besteht nun darin, dass mein Selektor nicht mehr funktioniert, seit ich den Filter als Parameter der Abfrage hinzugefügt habe.

Ich habe mir hier ähnliche Fragen angesehen wie: „Wie verwende ich den RTK-Abfrageselektor mit Parametern?“, aber ich bin zu dumm, um zu verstehen, was ich ändern sollte. Ich versuche, die RTK-Abfragedokumentation zu verstehen, aber es gelingt mir nicht.

Aus der obigen Frage weiß ich, dass mein Selektor auch Parameter haben muss, um genau zu wissen, was er auswählen soll, und dass dies kein empfohlenes Muster ist, aber ich verstehe nicht, wie ich es zum Laufen bringen soll.

Mein Einstiegs-Slice:

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

Ich verwende es in einer Eintragskomponente wie dieser

const {
    data: entriesData = [],
    refetch,
    isLoading,
    isSuccess,
    isError,
    error,
  } = useGetEntryQuery(filters)

  const entries = useSelector(selectEntry)

Hinweis: Wenn ich den „Filter“ aus der Get-Abfrage entferne, funktioniert alles wie zuvor (natürlich wie erwartet).

Haftungsausschluss: Ich habe keine Ahnung, was zum Teufel ich mache. Ich habe die Dokumentation gelesen und versuche es herauszufinden, daher bin ich für jedes Feedback sehr dankbar. Danke!

P粉729198207
P粉729198207

Antworte allen(1)
P粉779565855

是的,这是一个有点敏感的话题,因为 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

我知道这听起来很复杂,我几乎没有让它工作,所以尽量避免朝这个方向走:)

可以找到我一路上找到的一篇有用的文章这里,他们还描述了一些在组件级别创建选择器并记住它们的方法,但我还没有全部尝试过。看看吧,也许您会找到更简单的方法来解决您的特定问题。

Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage