React Query乐观更新时获取MongoDB生成ID的问题及优化咨询
解决方案:用临时ID实现乐观更新+避免重复创建数据
你当前的问题核心有两个:一是onMutate里手动调用了addNewHabit,导致mutation本身执行时又发了一次请求,所以重复创建了数据;二是乐观更新时拿不到真实ID,导致跳转路径异常。可以通过临时ID占位+请求成功后替换的方式解决:
步骤1:生成临时ID用于乐观更新
不用提前调用接口,先创建一个临时ID(比如用crypto.randomUUID()浏览器原生支持,或者用uuid库),把带临时ID的新habit先写入缓存,实现即时的乐观更新效果。
步骤2:移除onMutate里的重复请求
原来的onMutate里调用addNewHabit是多余的,因为React Query的mutation本身会自动执行这个函数,这就是导致重复创建数据的原因。
步骤3:请求成功后替换真实ID并跳转
在onSuccess回调里拿到接口返回的真实_id,替换缓存里对应临时ID的条目,然后再跳转到详情页,这时路径就不会是undefined了。
修改后的完整代码
// 可选择引入uuid库,或用浏览器原生的crypto.randomUUID() import { v4 as uuidv4 } from 'uuid'; const addNewHabit = async ({ userId, habitName, getFullYear }) => { try { const { data } = await axios.post(import.meta.env.VITE_API_URL, { userId, habitName, getFullYear, }); return data; // 返回完整数据,方便后续获取真实_id } catch (error) { console.log(error.message); throw error; // 抛出错误,让React Query的onError能正常捕获 } }; export const addHabitData = () => { const queryClient = useQueryClient(); const navigate = useNavigate(); // 假设使用react-router的导航钩子 return useMutation(addNewHabit, { onMutate: async (newHabit) => { await queryClient.cancelQueries("habits"); const previousHabitsData = queryClient.getQueryData("habits"); // 生成临时ID用于乐观更新占位 const tempId = uuidv4(); // 把带临时ID的新habit写入缓存 queryClient.setQueryData("habits", (oldQueryData) => { return [...oldQueryData, { ...newHabit, _id: tempId }]; }); // 返回上下文,供后续错误处理使用 return { previousHabitsData, tempId }; }, onSuccess: (responseData, _, context) => { const { tempId } = context; // 用真实ID替换缓存里的临时ID条目 queryClient.setQueryData("habits", (oldQueryData) => { return oldQueryData.map(habit => habit._id === tempId ? { ...habit, _id: responseData._id } : habit ); }); // 拿到真实ID后再跳转到详情页 navigate(`/habits/${responseData._id}`); }, onError: (_, __, context) => { // 请求失败时回滚缓存到之前的状态 queryClient.setQueryData("habits", context.previousHabitsData); }, onSettled: () => { queryClient.invalidateQueries("habits"); }, }); };
额外说明
- 若不想引入uuid库,现代浏览器可直接使用
crypto.randomUUID()生成临时ID,兼容性覆盖绝大多数场景。 - 必须在
addNewHabit中抛出错误,这样React Query才能触发onError回调完成缓存回滚。 - 导航操作放在
onSuccess中,确保只有请求成功时才跳转,避免错误场景下的无效跳转。
内容的提问来源于stack exchange,提问作者Amos
相关产品推荐
相关产品推荐

