Next.js中SWR数据更新后UI未同步 条目删除功能异常
Next.js 条目删除失效、UI不同步问题修复
核心问题定位
- 服务端PUT接口逻辑存在分支穿透问题:删除操作执行完后没有中断流程,会继续执行新增条目的
$push逻辑。删除请求的请求体不携带credits字段,实际会向数组推入undefined脏数据,导致删除操作失效。同时删除分支的判断条件写为if (data.index),当删除索引为0的第一条数据时,0会被判定为假值,直接跳过删除逻辑。 - 前端删除方法存在作用域错误:
.then()链中拿到的接口返回值没有在外层作用域声明,后续执行data.find()时根本拿不到返回结果。且当前接口仅回传请求体参数,没有返回更新后的全量数据,前端无法拿到最新的条目列表。 - 列表渲染数据源错误:渲染条目列表时遍历的是存储当前输入值的
credit状态,而非存储所有条目的totalCredit状态,导致索引和实际存储的数据不匹配,容易出现误删。 - 数据同步机制不完善:写操作完成后没有主动触发SWR缓存刷新,仅靠1秒间隔的轮询更新,会出现操作后UI短时间不同步的问题。
分步修复方案
1. 修复服务端PUT接口逻辑
将新增、删除逻辑完全拆分,每个分支执行完后直接返回更新后的完整文档,避免分支穿透,代码修改如下:
handler.put(async (req, res) => { const requestData = req.body // 删除逻辑分支 if (requestData.index !== undefined) { const { date, index } = requestData // 删除对应索引的数组元素 await req.db.collection('credits').updateOne( { date: date }, { $unset: { [`credits.${index}`]: 1 } } ) // 清理删除后产生的null占位 await req.db.collection('credits').updateOne( { date: date }, { $pull: { credits: null } } ) // 查询更新后的完整文档返回给前端 const updatedDoc = await req.db.collection('credits').findOne({ date: date }) return res.json(updatedDoc) } // 新增逻辑分支 if (requestData.credits !== undefined) { await req.db.collection('credits').updateOne( { date: requestData.date }, { $push: { credits: requestData.credits } }, { upsert: true } ) // 查询更新后的完整文档返回给前端 const updatedDoc = await req.db.collection('credits').findOne({ date: requestData.date }) return res.json(updatedDoc) } res.status(400).json({ error: '请求参数无效' }) })
2. 修复前端状态同步逻辑
首先从SWR中引入mutate方法,用于写操作完成后主动刷新缓存,替换原有SWR引入代码:
import useSWR, { mutate } from 'swr' // 组件内SWR配置保持原有逻辑即可 const { data } = useSWR('/api/hello', async (url) => { const response = await axios.get(url); return response.data; }, { refreshInterval: 1000, revalidateOnMount: true });
重写删除方法,用async/await规避链式调用的作用域问题,直接用接口返回的最新数据更新状态:
const handleItemDelete = async (itemToBeDeleted) => { const options = { method: "PUT", headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ date: ref.current, index: itemToBeDeleted }) } try { const res = await fetch(`api/hello?date=${ref.current}`, options) const updatedData = await res.json() // 更新本地条目状态 setTotalCredit(updatedData.credits || []) // 主动触发SWR缓存刷新,保证全局数据一致 mutate('/api/hello') } catch (err) { console.log('删除请求出错:', err) } }
3. 修复列表渲染逻辑
将遍历数据源改为存储全量条目的totalCredit,移除不必要的React.Children.toArray包裹,给循环项添加key属性避免DOM复用导致的错位:
{totalCredit.map((item, index) => { return item > 0 ? ( <div key={index}> <span style={{ color: 'green' }}> +{item}C </span> <button onClick={() => handleItemDelete(index)}> X </button> </div> ) : null })}
4. 优化新增条目逻辑的同步
修改原有新增方法,请求完成后用接口返回的最新数据更新状态,同时触发SWR刷新:
const handleClick = async (e) => { e.preventDefault(); const currentDate = ref.current const options = { method: "PUT", headers: { 'Content-type': 'application/json' }, body: JSON.stringify({ date: currentDate, credits: credit }) } try { const res = await fetch(`api/hello?date=${currentDate}`, options) const updatedData = await res.json() setTotalCredit(updatedData.credits || []) setCredit(0) mutate('/api/hello') } catch (err) { console.log('新增请求出错:', err) } }
优化提示
- 所有接口分支执行完成后必须加
return,避免逻辑穿透到下一个分支产生非预期结果 - 判断索引、数字类参数是否存在时,不要用真值判断,要用
!== undefined这类严格判断,避免0、空字符串等假值被误判 - 使用SWR时,所有增删改操作完成后主动调用
mutate刷新缓存,比固定间隔轮询的实时性更好,也能避免中间状态不一致 - 列表渲染统一使用和后端同步的全量状态作为数据源,不要用临时输入值遍历,避免索引和后端存储的数据错位
内容的提问来源于stack exchange,提问作者Berkay
相关产品推荐
相关产品推荐

