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

在NextJS中使用SWR进行数据变更的最优方案探讨

Next.js + SWR 获取用户数据的优化实现方案

针对你当前的实现,这里提供几个实用的优化方向,让代码更简洁、健壮且易于维护:

1. 统一请求工具,封装全局API客户端

当前代码混用了axios和fetch,建议统一用axios并封装全局实例,减少重复配置,还能统一处理请求拦截、错误:

// utils/apiClient.js
import axios from 'axios';

// 用环境变量替换硬编码地址,适配不同环境
const apiClient = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api',
});

// 可选:添加请求/响应拦截器,处理token、错误统一提示等
apiClient.interceptors.response.use(
  res => res,
  err => Promise.reject(err.response?.data?.message || err.message)
);

export default apiClient;

2. 抽离全局Fetcher函数,复用请求逻辑

把SWR的fetcher抽成单独模块,避免每个Hook重复编写,同时统一处理请求错误:

// utils/fetcher.js
import apiClient from './apiClient';

export const fetcher = async (url, options = {}) => {
  const res = await apiClient(url, options);
  return res.data;
};

3. 简化useUser Hook的实现

基于上面的封装,精简useUser,同时优化错误信息返回:

// utils/hooks/useUser.js
import useSWR from 'swr';
import { fetcher } from '../fetcher';

const useUser = (swrOptions = {}) => {
  // 默认SWR配置,可通过参数覆盖
  const defaultOptions = {
    revalidateOnFocus: true,
    revalidateOnReconnect: true,
  };

  const { data, error, isLoading, mutate } = useSWR(
    '/user',
    fetcher,
    { ...defaultOptions, ...swrOptions }
  );

  return {
    user: data,
    loading: isLoading,
    error: error?.message || error,
    mutate,
  };
};

export default useUser;

4. 优化删除操作的Mutate流程,处理失败回滚

当前先修改本地数据再请求后端,若请求失败会导致本地与后端数据不一致。建议用乐观更新+失败回滚的方式,兼顾体验和数据一致性:

// Dashboard组件中的删除按钮逻辑
<button
  onClick={async () => {
    if (!id) return;

    setModalOpen(false);
    // 保存原始数据,用于失败回滚
    const originalUser = user;
    // 乐观更新本地数据,不触发重新验证
    mutate(
      prev => ({
        ...prev,
        quizzes: prev.quizzes.filter(quiz => quiz.id !== id),
      }),
      false
    );

    try {
      // 请求后端删除
      await apiClient.delete(`/quiz?quizId=${id}`);
      // 成功后触发重新验证,确保数据完全同步
      await mutate();
    } catch (e) {
      console.error('删除失败:', e);
      // 失败回滚到原始数据
      mutate(originalUser, false);
      // 可选:给用户提示错误,比如用toast组件
      // toast.error('删除失败,请重试');
    } finally {
      setId(null);
    }
  }}
>
  删除
</button>

5. 完善UI边界状态处理

在Dashboard组件中添加加载、错误、无数据的状态反馈,避免用户看到空白或无提示的异常:

const Dashboard = () => {
  const [modalOpen, setModalOpen] = useState(false); 
  const [id, setId] = useState(null);
  const { user, loading, error, mutate } = useUser();

  // 加载状态
  if (loading) return <div>加载中...</div>;
  // 错误状态
  if (error) return <div>加载失败:{error}</div>;
  // 无用户数据的情况
  if (!user) return <div>未获取到用户信息</div>;

  return (
    <div>
      <h1>Dashboard</h1>
      {/* 渲染用户的测验列表 */}
      <ul>
        {user.quizzes.map(quiz => (
          <li key={quiz.id}>
            {quiz.title}
            <button onClick={() => {
              setId(quiz.id);
              setModalOpen(true);
            }}>删除</button>
          </li>
        ))}
      </ul>
      {/* 删除确认模态框 */}
      {modalOpen && (
        <div className="modal">
          <p>确定要删除这个测验吗?</p>
          <button onClick={/* 上面的删除逻辑 */}>确认删除</button>
          <button onClick={() => setModalOpen(false)}>取消</button>
        </div>
      )}
    </div>
  );
};

6. 配置环境变量

在项目根目录创建.env.local文件,存储API地址,方便不同环境切换:

NEXT_PUBLIC_API_URL=http://localhost:3000/api

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 23:33:19