调用/pages/api接口时React useTransition的isPending始终为true问题
useTransition的isPending始终为true,无法刷新React Server Component
调用/pages/api/clients的DELETE接口删除MongoDB记录后,useTransition的isPending一直保持true,按钮持续显示"Loading",React Server Component也没能按预期刷新。
错误原因分析
API接口未等待异步操作完成
服务端的deleteClientById是异步操作(MongoDB的删除方法均为异步),但接口handler直接调用后就返回响应,导致数据库删除还未完成时,前端就触发了router.refresh,此时数据未更新,RSC刷新后状态不一致,进而导致transition无法正常结束。Fetch请求格式错误
前端fetch的body直接传递details._id字符串,但Next.js API默认解析JSON格式的请求体,未设置正确的Content-Type头,可能导致后端无法正确获取ID,删除操作失败,间接引发transition异常。startTransition触发时机存在隐患
虽然await了fetch,但如果接口提前返回(未等数据库操作完成),此时调用router.refresh获取的仍是旧数据,React无法感知状态更新完成,isPending就会一直停留在true。
修复方案
1. 修复服务端API接口
确保等待数据库删除操作完成后再返回响应:
import type { NextApiRequest, NextApiResponse } from 'next'; import ClientsCollection from '../../db/collections/clients'; import Client from '../../helpers/interfaces/client'; type Data = { name: string; }; const clientsCollection = new ClientsCollection(); export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { switch (req.method) { case 'DELETE': { try { // 等待异步删除操作完成 const result = await clientsCollection.deleteClientById(req.body); res.status(200).json(result); } catch (error) { res.status(500).json({ error: '删除操作失败' }); } break; } default: res.status(403).json({ error: '不支持的请求方法' }); } }
2. 修正客户端Fetch请求格式
添加正确的请求头,序列化请求体:
'use client' const DeleteButton = ({ details }) => { const [isPending, startTransition] = useTransition(); const router = useRouter(); const handleDelete = async () => { try { // 修正fetch请求格式 await fetch('/api/clients', { method: 'DELETE', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(details._id), }); // 确保数据库操作完成后,触发transition刷新RSC startTransition(() => { router.refresh(); }); } catch (error) { console.error('删除失败:', error); // 可在此添加错误提示,isPending会自动恢复为false } } useEffect(() => { console.log('is pending ? ', isPending); }, [isPending]); return <Button onClick={handleDelete}>{ isPending ? 'Loading' : 'Delete' }</Button> }
额外注意点
- 确保
clientsCollection.deleteClientById内部正确返回Promise,且正确执行MongoDB删除操作(比如使用deleteOne或findByIdAndDelete并返回结果)。 - 若
router.refresh后RSC仍未刷新,检查RSC的数据获取逻辑是否为动态(如使用async/await从数据库拉取数据,而非静态生成)。
内容的提问来源于stack exchange,提问作者Ondřej Ševčík
相关产品推荐
相关产品推荐

