将 RTK 查询 API 与 redux 减速器和选择器连接

Eva*_*van 16 reactjs react-redux redux-toolkit rtk-query

我认为我缺少有关 Redux 和 RTK 查询工具的一些东西。我还为此使用 RTK Query OpenAPI codegen。

现在我有一个如下所示的 API ->

export const api = generatedApi.enhanceEndpoints({
  endpoints: {
    getMaterials: {
      transformResponse: response => normalize(response),
    },
  },
})
Run Code Online (Sandbox Code Playgroud)

这在我的组件中很好地返回了标准化数据:

const Materials = () => {
  const { data, isLoading, error } = useGetMaterialsQuery()

  /*
    Cool this gives me data back like:
    {
      [id]: {
        id,
        prop1: 'blah',
        prop2: 'blah2'
      }
    }
  */

  console.log(data)

  // but now I want to structure this data differently using selector

  const newDataStructure = useSelector(addSomeMetaDataAndStructureDifferentlySelector)

  return <MyComponent structuredData={newDataStructure} />
}
Run Code Online (Sandbox Code Playgroud)

但是当我查看该选择器中的状态时,它看起来像:

数据片段

当在选择器中时,我真的想使用类似 -> 的东西

const addSomeMetaDataAndStructureDifferentlySelector = state => 
    // create new data structure from state.materials.byId that I will pass to `MyComponent`
Run Code Online (Sandbox Code Playgroud)

我的商店现在看起来像这样 ->

import { configureStore } from '@reduxjs/toolkit'
import { setupListeners } from '@reduxjs/toolkit/query'
import { api } from 'gen/rtk-openapi'
// import materialsReducer from 'state/materials/materialsSlice'

const store = configureStore({
  reducer: {
    [api.reducerPath]: api.reducer,
  },
  middleware: getDefaultMiddleware =>
    getDefaultMiddleware().concat(api.middleware),
})

setupListeners(store.dispatch)

export default store
Run Code Online (Sandbox Code Playgroud)

那么我是否需要data直接将 from传递useGetMaterialsQuery到函数中并对其进行转换?

或者我可以以某种方式创建一个新的减速器切片,以某种方式正确初始化数据,以便我可以state.materials.byId在选择器中访问?

或者我从根本上错过了一些东西?

phr*_*hry 20

您错过了重要的一点:RTK-Query 不是标准化缓存,而是文档缓存。

它使用请求参数作为缓存键来缓存您的端点以及对这些端点的每个请求。这些响应中的每一个都被视为“文档”并单独缓存。

这似乎是一个缺点,但作为一个通用库,做一个真正的“标准化”缓存几乎是不可能的,特别是对于像 REST api 这样不明确的东西。即使对于 GraphQL 来说,这也是一个非常困难的问题,甚至专门针对 GraphQL 的解决方案(如 apollo)也有很多问题,并且最终会产生大量手写代码来处理集合中添加/删除等情况。

因此,最后,您有两个选择:手动编写自己的规范化缓存解决方案,明确适合您的数据结构或使用文档缓存。

在大多数情况下,文档缓存就足够了。这就是 RTK-Query 的用武之地,它试图为您提供尽可能多的工具来保持文档之间的数据“同步”:自动重新获取以及您希望避免执行手动乐观更新来操作特定缓存条目的选项。

一般来说,我建议不要将数据复制到手写切片中。这样您将失去 RTK-Query 的许多好处,例如订阅跟踪和 60 秒后没有更多组件使用值后的自动缓存清理。在使用 Redux 和手写切片一段时间后,这听起来可能不太常见,但它在大多数情况下都工作得很好 - 代码少了很多。

至于如何使用:一般来说,你只需useGetMaterialsQuery()在所有需要该材料的组件中调用你的组件,然后在你的组件中过滤掉你需要的东西,或者使用选项selectFromResult来选择你真正需要的东西。

  • `const result = useGetMaterialsQuery(undefined, { selectFromResult: result =&gt; ({ ...result, data: result.data.foo.bar }) })` (2认同)
  • 这个答案对我来说很有意义,但我仍然在努力找出 rtk-query 中存储组件所需的文档缓存更改的位置。我知道将缓存复制到另一个切片是不受欢迎的,所以我应该创建更多自定义挂钩来添加/过滤/更新值(客户端仅不会返回到服务器),还是应该对我的业务逻辑进行任何调整服务器响应在类似“transformResponse”的地方?例如,如果我想将对象响应上的两个字段组合成一个新的键值,那么在哪里发生呢? (2认同)
  • @aboutaaron,如果您在任何地方使用该端点,则以相同的形式使用它,然后在“transformResponse”中使用它。如果一个组件只需要响应的一部分,我会使用“selectFromResult”,如果它需要在此基础上进行数据转换,我只需“useMemo”。 (2认同)