RTK Query中仅更新单个用户时Optimistic updates失效求助
问题描述
基于RTK Query搭建CRUD接口实现乐观更新时,调用updateQueryData更新getSingleUser缓存时,onQueryStarted中的draft函数完全不执行;但更新getAllUsers缓存时乐观更新正常工作,期望实现单个用户更新时的乐观更新。
代码示例
原问题代码
import { EntityState, createEntityAdapter } from '@reduxjs/toolkit' import api from '../api' export type User = { userId: number name: string username: string } const usersAdaptor = createEntityAdapter<User>({ selectId: (model) => model.userId, }) const initialState = usersAdaptor.getInitialState() const extendedUsersAPI = api.injectEndpoints({ endpoints: (builder) => ({ getAllUsers: builder.query<EntityState<User>, undefined | void>({ query: () => ({ url: '/users', method: 'GET' }), transformResponse: (result: User[]) => usersAdaptor.setAll(initialState, result), providesTags: (result?: EntityState<User>) => result ? [ { type: 'Users', id: 'LIST' }, ...result.ids.map((id) => ({ type: 'Users' as const, id, })), ] : [{ type: 'Users', id: 'LIST' }], }), getSingleUser: builder.query<EntityState<User>, number>({ query: (userId) => ({ url: '/users/' + userId, method: 'GET' }), transformResponse: (result: User) => usersAdaptor.setOne(initialState, result), providesTags: (_result, _meta, id) => [{ type: 'Users', id }], }), updateUser: builder.mutation<void, User>({ query: (user) => ({ url: `/users/${user.userId}`, method: 'PUT', data: user, }), async onQueryStarted( { userId, ...patch }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( extendedUsersAPI.util.updateQueryData( 'getSingleUser', userId, (draft) => { console.log('sc') // <------------ never runs! Object.assign(draft, patch) } ) ) try { await queryFulfilled } catch { patchResult.undo() } }, }), addNewUser: builder.mutation<EntityState<User>, Partial<User>>({ query: (user) => ({ url: '/users', method: 'POST', data: user }), invalidatesTags: (_result, _error, arg) => [ { type: 'Users', id: arg.userId }, ], }), }), }) export const { useGetAllUsersQuery, useLazyGetAllUsersQuery, useAddNewUserMutation, useUpdateUserMutation, } = extendedUsersAPI
可正常运行的修改(更新getAllUsers)
{ // ... updateUser: builder.mutation<void, User>({ query: (user) => ({ url: `/users/${user.userId}`, method: 'PUT', data: user, }), async onQueryStarted( { userId, ...patch }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( extendedUsersAPI.util.updateQueryData( 'getAllUsers', undefined, (draft) => { console.log('sc') // <------------ this runs! Object.assign(draft, patch) } ) ) try { await queryFulfilled } catch { patchResult.undo() } }, }), }
问题原因
- 缓存不存在:
updateQueryData仅会在目标查询(这里是getSingleUser)已经被执行过且结果存在于缓存中时,才会调用draft函数。如果当前页面没有先调用getSingleUser获取对应userId的数据,缓存中没有该查询的条目,draft函数自然不会执行。 - 更新方式错误:即使缓存存在,原代码中直接
Object.assign(draft, patch)是错误的——因为draft是EntityState类型(包含ids和entities),而非单个User对象,直接赋值无法更新到正确的实体数据。
解决方案
步骤1:确保getSingleUser缓存存在
在执行updateUser之前,确保已经通过useGetSingleUserQuery或useLazyGetSingleUserQuery获取过目标用户的数据,让其结果存入缓存。
步骤2:正确更新EntityState中的用户数据
使用usersAdaptor提供的updateOne方法来修改draft中的实体,而不是直接操作draft对象:
updateUser: builder.mutation<void, User>({ query: (user) => ({ url: `/users/${user.userId}`, method: 'PUT', data: user, }), async onQueryStarted( { userId, ...patch }, { dispatch, queryFulfilled } ) { const patchResult = dispatch( extendedUsersAPI.util.updateQueryData( 'getSingleUser', userId, (draft) => { console.log('sc') // 现在会执行了 // 使用adaptor的updateOne方法更新实体 usersAdaptor.updateOne(draft, { id: userId, changes: patch }) } ) ) try { await queryFulfilled } catch { patchResult.undo() } }, }),
可选:强制缓存预加载(如果需要)
如果无法保证getSingleUser已被调用,可以在updateUser的onQueryStarted中先触发getSingleUser的查询(使用dispatch(extendedUsersAPI.endpoints.getSingleUser.initiate(userId))),确保缓存存在后再执行更新,但这种方式可能会额外发起请求,需根据业务场景权衡。
内容的提问来源于stack exchange,提问作者Reza
相关产品推荐
相关产品推荐

