在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
相关产品推荐
相关产品推荐

