将 RTK 查询 API 与 redux reducer 和 selector 连接起来
Connecting RTK Query API with redux reducer and selector
我想我遗漏了一些关于 Redux 和 RTK 查询工具的东西。为此我也在使用 RTK Query OpenAPI codegen。
现在我有一个看起来像这样的 API ->
export const api = generatedApi.enhanceEndpoints({
endpoints: {
getMaterials: {
transformResponse: response => normalize(response),
},
},
})
这在我的组件中返回正常的规范化数据:
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} />
}
但是当我查看该选择器中的状态时,它看起来像:
在选择器中我真的想使用类似 ->
的东西
const addSomeMetaDataAndStructureDifferentlySelector = state =>
// create new data structure from state.materials.byId that I will pass to `MyComponent`
我的商店现在看起来像这样 ->
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
那么我是否需要将 data
直接从 useGetMaterialsQuery
传递到一个函数中并对其进行转换?
或者我能否以某种方式创建一个新的 reducer 切片,以某种方式正确初始化数据,以便我可以在选择器中访问 state.materials.byId
?
或者我在这里从根本上遗漏了什么?
您错过了重要的一点:RTK-Query is not a normalized cache,但是文档缓存。
它使用请求参数作为缓存键来缓存您的端点和对这些端点的每个请求。这些响应中的每一个都被视为“文档”并单独缓存。
这似乎是一个缺点,但作为一个通用库,做一个真正的“规范化”缓存几乎是不可能的,尤其是对于像 REST api 这样模棱两可的东西。即使对于 GraphQL 来说,这也是一个非常困难的问题,甚至专门针对 GraphQL 的解决方案(如 apollo)也有很多问题,最终需要大量手写代码来处理像 additions/removals 来自集合的情况。
所以最后,您有两个选择:手动编写自己的规范化缓存解决方案,明确针对您的数据结构或使用文档缓存。
在大多数情况下,文档缓存就足够了。这就是 RTK-Query 的用武之地,它会尝试为您提供尽可能多的工具,让您的文档之间的数据保持“同步”:automated refetching and where you want to avoid that the option to do manual optimistic updates 来操作特定的缓存条目。
一般来说,我建议不要将该数据复制到手写切片。这样您将失去 RTK-Query 的很多好处,例如订阅跟踪和在 60 秒后不再有组件使用值后的自动缓存清理。
在将 Redux 与手写切片一起使用一段时间后,这听起来可能并不常见,但它在大多数情况下都能很好地工作——代码少得多。
至于使用方法:一般在所有需要素材的组件中调用你的useGetMaterialsQuery()
,在你的组件中过滤掉你需要的就可以了,或者使用selectFromResult
选项只是 select 你真正需要的。
我想我遗漏了一些关于 Redux 和 RTK 查询工具的东西。为此我也在使用 RTK Query OpenAPI codegen。
现在我有一个看起来像这样的 API ->
export const api = generatedApi.enhanceEndpoints({
endpoints: {
getMaterials: {
transformResponse: response => normalize(response),
},
},
})
这在我的组件中返回正常的规范化数据:
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} />
}
但是当我查看该选择器中的状态时,它看起来像:
在选择器中我真的想使用类似 ->
的东西const addSomeMetaDataAndStructureDifferentlySelector = state =>
// create new data structure from state.materials.byId that I will pass to `MyComponent`
我的商店现在看起来像这样 ->
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
那么我是否需要将 data
直接从 useGetMaterialsQuery
传递到一个函数中并对其进行转换?
或者我能否以某种方式创建一个新的 reducer 切片,以某种方式正确初始化数据,以便我可以在选择器中访问 state.materials.byId
?
或者我在这里从根本上遗漏了什么?
您错过了重要的一点:RTK-Query is not a normalized cache,但是文档缓存。
它使用请求参数作为缓存键来缓存您的端点和对这些端点的每个请求。这些响应中的每一个都被视为“文档”并单独缓存。
这似乎是一个缺点,但作为一个通用库,做一个真正的“规范化”缓存几乎是不可能的,尤其是对于像 REST api 这样模棱两可的东西。即使对于 GraphQL 来说,这也是一个非常困难的问题,甚至专门针对 GraphQL 的解决方案(如 apollo)也有很多问题,最终需要大量手写代码来处理像 additions/removals 来自集合的情况。
所以最后,您有两个选择:手动编写自己的规范化缓存解决方案,明确针对您的数据结构或使用文档缓存。
在大多数情况下,文档缓存就足够了。这就是 RTK-Query 的用武之地,它会尝试为您提供尽可能多的工具,让您的文档之间的数据保持“同步”:automated refetching and where you want to avoid that the option to do manual optimistic updates 来操作特定的缓存条目。
一般来说,我建议不要将该数据复制到手写切片。这样您将失去 RTK-Query 的很多好处,例如订阅跟踪和在 60 秒后不再有组件使用值后的自动缓存清理。 在将 Redux 与手写切片一起使用一段时间后,这听起来可能并不常见,但它在大多数情况下都能很好地工作——代码少得多。
至于使用方法:一般在所有需要素材的组件中调用你的useGetMaterialsQuery()
,在你的组件中过滤掉你需要的就可以了,或者使用selectFromResult
选项只是 select 你真正需要的。