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

卡拉OK报名应用SWR乐观UI实现异常求助

问题解决:SWR乐观UI实现优化

核心问题分析

你遇到的歌曲先消失再出现的问题,主要源于乐观更新的数据构建错误和mutate调用方式不当:

  • 手动构建newObjectUpdated时,可能未正确深拷贝原数据或精准定位目标歌曲,导致缓存数据结构异常
  • 两次调用mutate都传入相同的乐观数据,第二次本该用服务器返回的真实数据更新,却重复使用乐观数据,引发UI闪烁

修复方案

1. 用函数式更新构建乐观数据

利用SWR mutate的函数式参数,基于当前缓存的最新数据生成乐观更新后的结构,避免手动构建数据出错:

const { mutate } = useSWRConfig();

const isSongPlayed = async (song: Song, bool: boolean) => {
  // 执行乐观更新:基于当前缓存数据修改
  const optimisticUpdate = mutate(`/api/events/${id}`, (currentEvent) => {
    if (!currentEvent) return currentEvent;
    
    // 深拷贝原数据,避免修改缓存原对象
    const updatedEvent = JSON.parse(JSON.stringify(currentEvent));
    // 定位到目标歌曲并更新状态
    updatedEvent.moments.forEach(moment => {
      const targetSong = moment.songs.find(s => s._id === song._id);
      if (targetSong) {
        targetSong.played = bool;
      }
    });
    return updatedEvent;
  }, {
    optimisticData: true,
    rollbackOnError: true, // 请求失败自动回滚到更新前状态
    populateCache: true
  });

  try {
    const response = await fetch(`/api/songs/${song._id}`, {
      method: "PUT",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ ...song, played: bool }),
    });
    const playedSong = await response.json();

    if (!playedSong.success || !playedSong.data) {
      setLoading(true);
      optimisticUpdate?.rollback(); // 手动触发回滚
      return;
    }

    // 请求成功后,让SWR重新验证数据,确保缓存与服务器一致
    mutate(`/api/events/${id}`);
  } catch (error) {
    console.error(error);
    // 可在此添加用户可见的错误提示,比如Toast
  }
};

const onSetAsPlayed = (song: Song) => {
  isSongPlayed(song, !song.played); // 简化状态切换逻辑
};

2. 关键优化点说明

  • 函数式更新:通过(currentEvent) => {...}直接基于当前缓存生成新数据,避免手动构建newObjectUpdated时的结构错误
  • 自动回滚:设置rollbackOnError: true,请求失败时SWR会自动将缓存恢复到更新前状态,避免UI停留在错误状态
  • 深拷贝数据:用JSON.parse(JSON.stringify(currentEvent))深拷贝原数据,防止直接修改缓存对象导致的状态混乱(若数据包含函数或特殊类型,可改用lodash的cloneDeep)
  • 请求成功后重新验证:最后调用mutate(/api/events/${id})让SWR从服务器拉取最新数据,确保缓存与后端完全一致

3. 额外优化建议

  • 不要直接修改传入的song对象:onSetAsPlayed中通过参数传递新状态,避免污染原数据
  • 加载状态优化:改用SWR自带的isLoading状态,或给mutate添加onLoading回调,减少手动维护setLoading的成本
  • 错误反馈:在catch块中添加用户可见的错误提示,提升体验

内容的提问来源于stack exchange,提问作者Tiago Pereira

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 19:25:42