You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 22:36:23