React-admin自定义数据提供者下已删记录仍显示在列表的问题
我实现了React-Admin自定义数据提供者的delete方法如下:
delete: (resource, { id, previousData }) => { return deleteRecord(id).then(res => { if (res.data.deleteRecord.success) { return { data: previousData }; } }); },
deleteRecord通过Apollo Client成功删除了远程数据,React-Admin会跳转到列表页并显示带“撤销”按钮的通知,但通知消失后,已删除的记录又重新出现在列表里。必须刷新窗口才能看到正确数据,React-Admin自带的刷新功能也没用。
同样的问题在调用deleteMany时也出现,我的deleteMany实现:
deleteMany: (resource, { ids }) => { return Promise.all( ids.map(id => { return deleteRecord(id).then(res => { if (res.data.deleteRecord.success) { return id; } }); }) ).then(data => ({ data })); },
我希望列表本地状态能和数据提供者的删除操作保持一致,试过调整mutationOptions、自定义AppBar、onSuccess和afterDelete生命周期,都没解决。
问题根源
这事儿核心是Apollo Client的缓存没同步更新。你用deleteRecord删掉了远程数据,但Apollo默认不会自动移除缓存里的对应记录。当React-Admin的列表组件重新执行查询时,Apollo优先从缓存读数据,就把已经删掉的记录又加载出来了。
React-Admin本身会根据数据提供者的返回更新本地状态,但如果底层Apollo缓存里还留着旧数据,列表查询会覆盖React-Admin的本地状态,导致记录“复活”。
解决方案
方案1:删除后手动更新Apollo缓存
修改deleteRecord函数,在mutation成功后,用Apollo的cache.evict和writeQuery方法移除缓存中的记录,同时更新列表查询的缓存。
单条删除适配
假设你的列表查询叫GetPosts,数据类型的__typename是Post,根据实际情况替换:
import { gql } from '@apollo/client'; // 替换成你项目中实际的列表查询语句 const GET_POSTS = gql` query GetPosts($pagination: PaginationInput, $sort: SortInput, $filter: FilterInput) { posts(pagination: $pagination, sort: $sort, filter: $filter) { id // 其他需要的字段 } } `; const deleteRecord = async (id) => { const { data } = await apolloClient.mutate({ mutation: gql` mutation DeleteRecord($id: ID!) { deleteRecord(id: $id) { success } } `, variables: { id }, update(cache) { // 从缓存中移除单条记录 cache.evict({ id: cache.identify({ __typename: 'Post', id }) }); // 更新列表缓存,移除已删除的记录 try { // 读取当前列表的缓存数据 const existingPosts = cache.readQuery({ query: GET_POSTS }); if (existingPosts) { cache.writeQuery({ query: GET_POSTS, data: { posts: existingPosts.posts.filter(post => post.id !== id) } }); } } catch (err) { // 如果缓存里没有该列表的查询结果,忽略错误即可 } } }); return { data }; };
批量删除适配
批量删除时不需要额外修改deleteMany方法,上面的deleteRecord已经在单条删除时处理了缓存更新,批量调用时会自动逐个清理缓存中的对应记录。
方案2:禁用列表查询的缓存(临时方案)
如果不想手动维护缓存,可以给列表查询加上fetchPolicy: 'network-only',强制每次都从远程拉取最新数据。但这会增加网络请求次数,影响性能,仅适合临时测试:
// 在执行列表查询的代码中添加fetchPolicy apolloClient.query({ query: GET_POSTS, variables: { /* 你的分页、排序、过滤参数 */ }, fetchPolicy: 'network-only' });
注意事项
- 务必把代码里的
__typename(比如Post)和查询名称(比如GetPosts)替换成你项目中实际使用的内容 - 如果列表查询带有动态参数(如分页页码、过滤条件),调用
cache.readQuery时要传入对应的变量,才能正确读取缓存的列表数据 - 你当前的
delete和deleteMany方法返回格式完全符合React-Admin的要求,无需修改这部分逻辑
内容的提问来源于stack exchange,提问作者Simon Mulquin

