Redux Toolkit createApi如何合并解析多个接口端点的返回结果?
Redux Toolkit createApi多端点返回结果合并最佳实践
方案1:使用记忆化选择器(官方优先推荐)
该方案不需要额外编写reducer,所有合并逻辑放在数据层,完全不会污染视图代码,性能有保障。
- 实现原理:通过
createSelector创建记忆化派生数据选择器,从createApi的缓存中直接读取两个端点的返回结果,合并后供视图层调用,只有依赖的原始数据发生变化时才会重新计算合并结果。 - 实现代码示例:
// apiSlice.js 你的createApi定义文件 import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' import { createSelector } from '@reduxjs/toolkit' export const apiSlice = createApi({ reducerPath: 'api', baseQuery: fetchBaseQuery({ baseUrl: '/api' }), endpoints: (builder) => ({ getUsers: builder.query({ query: () => '/users' }), getCars: builder.query({ query: () => '/cars' }) }) }) export const { useGetUsersQuery, useGetCarsQuery } = apiSlice // 定义两个端点的缓存结果选择器 const selectUsers = apiSlice.endpoints.getUsers.select() const selectCars = apiSlice.endpoints.getCars.select() // 定义合并逻辑的记忆化选择器 export const selectUserWithCars = createSelector( [selectUsers, selectCars], (usersRes, carsRes) => { // 仅在两个接口都请求成功时执行合并 if (usersRes.isSuccess && carsRes.isSuccess) { // 此处替换为你的业务关联逻辑,示例为按用户ID关联名下车辆 return usersRes.data.map(user => ({ ...user, cars: carsRes.data.filter(car => car.userId === user.id) })) } return [] } )
- 视图层调用示例:
import { useSelector } from 'react-redux' import { useGetUsersQuery, useGetCarsQuery, selectUserWithCars } from './apiSlice' function MixedPage() { // 触发两个接口请求,确保缓存中存在数据 const { isLoading: usersLoading, error: usersErr } = useGetUsersQuery() const { isLoading: carsLoading, error: carsErr } = useGetCarsQuery() // 直接获取合并后的数据,无需在视图层写任何合并逻辑 const mergedList = useSelector(selectUserWithCars) if (usersLoading || carsLoading) return <div>加载中</div> if (usersErr || carsErr) return <div>请求异常</div> return ( <ul> {mergedList.map(item => ( <li key={item.id}> <p>用户名:{item.name}</p> <div> {item.cars.map(car => <img key={car.id} src={car.imgUrl} alt={car.name} />)} </div> </li> ))} </ul> ) }
方案2:自定义Slice存储合并结果
如果需要对合并后的数据做独立的增删改操作,可以采用该方案,通过extraReducers监听createApi的请求成功回调,自动合并数据存入自定义slice。
- 实现代码示例:
// mergedDataSlice.js import { createSlice } from '@reduxjs/toolkit' import { apiSlice } from './apiSlice' const mergedDataSlice = createSlice({ name: 'mergedUserCars', initialState: { data: [], status: 'idle' }, reducers: {}, extraReducers: (builder) => { // 监听用户接口请求成功事件 builder.addMatcher( apiSlice.endpoints.getUsers.matchFulfilled, (state, { payload: users, meta }) => { const carsRes = apiSlice.endpoints.getCars.select()(meta.arg.getState()) if (carsRes.isSuccess) { // 执行合并逻辑 state.data = users.map(user => ({ ...user, cars: carsRes.data.filter(car => car.userId === user.id) })) state.status = 'success' } } ) // 监听车辆接口请求成功事件 .addMatcher( apiSlice.endpoints.getCars.matchFulfilled, (state, { payload: cars, meta }) => { const usersRes = apiSlice.endpoints.getUsers.select()(meta.arg.getState()) if (usersRes.isSuccess) { state.data = usersRes.data.map(user => ({ ...user, cars: cars.filter(car => car.userId === user.id) })) state.status = 'success' } } ) } }) export default mergedDataSlice.reducer
- 注意事项:需要将该slice的reducer注册到项目的root reducer中,视图层通过
useSelector(state => state.mergedUserCars.data)即可直接读取合并后的数据。
方案选择建议
- 绝大多数场景优先选择方案1,无需额外维护状态,代码量更少,记忆化选择器的性能足以支撑常规业务需求。
- 仅当需要对合并后的数据做独立修改、且无需同步回原始接口缓存时,再选择方案2。
内容的提问来源于stack exchange,提问作者François Richard
相关产品推荐
相关产品推荐

