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

升级React 18至createRoot导致TanStack/query突变失效求助

React 18 createRoot下TanStack Query注册后自动登录失效问题解决

我使用TanStack/Query处理Drupal 9网站的注册/登录逻辑,原本设计为注册成功后自动为用户登录。在React 17中以下代码可正常完成注册并立即登录,但升级到React 18并使用createRoot后,注册成功但登录失败——onSuccess回调中无法访问formData变量,打印显示formData为空对象。

原代码:

const onSubmit = (submitData: DrupalLoginFormInput) => {
  setIsLoading(true);
  mutateRegister.mutate(submitData);
};

export const useMutationRegister = (
  formData: DrupalLoginFormInput,
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>,
): UseMutationResult<HttpResponse, string, DrupalLoginFormInput> => {
  const mutateLogin = useMutationLogin();
  return useMutation(
    (registerData: DrupalLoginFormInput) =>
      postRegisterUser(registerData, platform),
    {
      onSuccess: () => {
        // console.log('mutateRegister succeeded', response, formData);
        // Log in with the newly registered info.
        const registeredDataToLogin = {
          mail: formData.mail,
          pass: formData.pass,
        };
        mutateLogin.mutate(registeredDataToLogin);
      },
    },
  );
};

export type DrupalLoginFormInput = {
  mail: string;
  pass: string;
};

我曾参考React 18批处理相关文档尝试用flushSync禁用批处理:

const onSubmit = (submitData: DrupalLoginFormInput) => {
  flushSync(() => {
    setIsLoading(true);
    mutateRegister.mutate(submitData);
  });
};

但登录仍失效(onSuccess中仍无法获取formData)。若保留React 18但换回旧的render(<App />, container)方式则登录恢复正常,但我希望使用createRoot以解锁React 18新特性,请问如何修改代码确保onSuccess访问formData时变量不丢失?


解决方案

问题根源在于闭包捕获的变量过期:React 18的并发渲染机制下,onSuccess回调捕获的是组件渲染时的formData旧值,而非注册时传入的最新数据。正确的做法是直接使用TanStack Query提供的mutation触发变量,而非依赖外部传入的formData。

修改后的代码

  1. 调整useMutationRegister,移除不必要的formData参数,在onSuccess中通过第二个参数获取注册时的原始数据:
export const useMutationRegister = (
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>,
): UseMutationResult<HttpResponse, string, DrupalLoginFormInput> => {
  const mutateLogin = useMutationLogin();
  return useMutation(
    (registerData: DrupalLoginFormInput) =>
      postRegisterUser(registerData, platform),
    {
      onSuccess: (_, variables) => {
        // 使用mutation触发时传入的原始变量,而非外部formData
        const registeredDataToLogin = {
          mail: variables.mail,
          pass: variables.pass,
        };
        mutateLogin.mutate(registeredDataToLogin);
      },
      onSettled: () => {
        // 注册请求完成(无论成功失败)后更新加载状态
        setIsLoading(false);
      }
    },
  );
};
  1. onSubmit函数保持不变:
const onSubmit = (submitData: DrupalLoginFormInput) => {
  setIsLoading(true);
  mutateRegister.mutate(submitData);
};

原理说明

TanStack Query的useMutation中,onSuccess回调的第二个参数variables就是触发mutation时传入的原始数据,它由Query内部保存,不受React组件渲染闭包的影响。直接使用这个变量就能确保拿到的是注册时的准确账号密码,彻底避免闭包导致的变量丢失问题。

之前尝试的flushSync无效,是因为问题本质不是React的批处理机制,而是闭包捕获的变量过期,所以禁用批处理无法解决根本问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 11:06:48