React Query结合json-server如何实现单请求批量PATCH多ID资源
解决方案
先明确json-server的原生能力边界
原生开箱即用的json-server不支持单请求批量更新多条资源,它默认遵循标准REST路由设计,单条资源更新只能走PATCH /{资源名}/{资源id}的路径,没有内置批量操作的路由。但可以通过两种路径优化现有实现,完全解决你提到的性能和重复触发缓存失效的问题。
方案1:不改动json-server,纯前端逻辑优化
这个方案不需要改后端服务,调整现有前端代码就能解决90%的问题:
统一等待所有更新请求完成后再触发缓存失效
你现在的核心问题是把缓存失效逻辑写在了单个useTakeSeat的onSuccess回调里,每完成一个座位的更新就会触发一次缓存刷新。只需要把所有更新请求的Promise收集起来,用Promise.all等待所有请求全部成功后,再执行一次缓存失效即可。
改造后的sendRequest逻辑参考:const sendRequest = useCallback(async () => { try { // 收集所有更新请求的Promise const updateTasks = selected.map((item) => takeSeat.mutateAsync({ id: item.id, hall: "hallA" }) ); // 等待所有请求完成 await Promise.all(updateTasks); // 所有请求成功后统一刷新一次缓存 useGetHallLayout.invalidate(); setSelected([]); } catch (err) { // 这里加错误提示,比如部分座位预订失败 console.error("预订失败", err); } }, [selected, takeSeat]);记得把
useTakeSeat初始化时传入的onSuccess回调删掉,避免重复触发失效。加乐观更新提升体验
不需要等接口返回再刷新数据,用户点击预订按钮时直接手动修改React Query的本地缓存,把选中座位的状态改成已预订,后台静默发送更新请求,请求失败时再把缓存回滚即可,用户感知不到请求等待时间。核心实现就是在发请求前调用queryClient.setQueryData直接更新本地缓存:// 发请求前先更新本地缓存 queryClient.setQueryData(["hall", "hallA"], (oldData) => { return oldData.map(seat => { const isSelected = selected.some(s => s.id === seat.id); return isSelected ? {...seat, available: false} : seat }) })修复现有代码的小隐患
- 你的
useGetHallLayout的queryKey目前只写了["hall"],如果后续新增hallB、hallC等多个影厅,不同影厅的缓存会互相覆盖,建议改成["hall", hall],缓存失效时也对应指定queryKey,避免误刷新其他影厅的数据。 handleSelect回调里用了函数式状态更新,完全不需要把selected加到useCallback依赖里,去掉后可以避免回调不必要的重创建。
- 你的
方案2:给json-server加10行代码实现批量更新接口
如果不想前端发送多个并发请求,可以给json-server加一个极简的自定义批量更新路由,改动量非常小:
- 新建一个自定义的server入口文件,比如
server.js:const jsonServer = require('json-server'); const server = jsonServer.create(); const router = jsonServer.router('db.json'); const middlewares = jsonServer.defaults(); server.use(middlewares); server.use(jsonServer.bodyParser); // 自定义批量更新接口 server.patch('/:hall/batch', (req, res) => { const { hall } = req.params; const { ids } = req.body; // 接收要更新的座位id数组 const db = router.db; // 批量把对应座位的available改成false db.get(hall).forEach(seat => { if (ids.includes(seat.id)) { seat.available = false; } }).write(); // 返回更新后的全量影厅数据 res.jsonp(db.get(hall).value()); }) server.use(router); server.listen(3000, () => { console.log('JSON Server is running on port 3000'); }) - 启动服务时不再直接跑
json-server --watch db.json,而是跑node server.js。 - 前端新增一个批量更新的mutation,预订时只需要发一次请求,把选中的座位id数组传过去即可,请求完成后只需要触发一次缓存失效,性能最优。
内容的提问来源于stack exchange,提问作者Przemo
相关产品推荐
相关产品推荐

