我正在使用 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
我知道这听起来很复杂,我几乎没有让它工作,所以尽量避免朝这个方向走:)
可以找到我一路上找到的一篇有用的文章这里,他们还描述了一些在组件级别创建选择器并记住它们的方法,但我还没有全部尝试过。看看吧,也许您会找到更简单的方法来解决您的特定问题。