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

如何使用React-query避免同一查询被多次调用?

React Query 登录/登出自定义Hook优化方案

问题场景

我在React应用中用React Query写了基于useQuery的useLogin和useLogout两个自定义Hook:

export const useLogin = (input?: LoginWithEmailPassword) => {
  const { data, isLoading, isSuccess, error } = useQuery(
    ["loginWithEmailPassword"],
    () => loginWithEmailPassword(input),
    {
      enabled: !!input,
    }
  );

  return { data: data?.data, isLoading, isSuccess, error };
};

export const useLogout = (accessToken?: string) => {
  const { isSuccess, isLoading, error } = useQuery(
    ["logout"],
    () => logout(accessToken),
    {
      enabled: !!accessToken,
    }
  );

  return { isSuccess, isLoading, error };
};

在AuthProvider组件里使用这两个Hook,通过维护loginInput和logoutInput状态来触发查询:

// AuthProvider.tsx
const [loginInput, setLoginInput] = useState<LoginWithEmailPassword>();
const [logoutInput, setLogoutInput] = useState<string | undefined>();

const { data, isSuccess: isLoginSuccess, isLoading } = useLogin(loginInput);
const { ...logoutProps } = useLogout(logoutInput);

const login = (input: LoginWithEmailPassword) => {
  setLoginInput(input);
};

const logout = () => {
  setLogoutInput(data?.accessToken);
};

但遇到一个问题:用户点击登录后,loginInput状态被设置,useLogin会执行查询,但因为loginInput状态一直存在,组件每次重渲染(比如点击登出时)useLogin都会重新执行一遍。我想找最佳解决办法,要是React Query有类似Apollo的useLazyQuery就更直观了。目前想到一个临时方案:登录成功后把loginInput重置为undefined:

useEffect(()=>{
  if(isLoginSuccess && !isLoading && !!data) setLoginInput(undefined)
},[isLoginSuccess])

最佳解决方案

1. 改用useMutation处理登录/登出(推荐)

登录和登出属于修改服务器状态的操作,React Query中这类场景应该用useMutation而不是useQuery,它天生就是用来处理一次性的写操作,不需要依赖状态来控制执行,直接手动调用mutate触发即可:

重写登录/登出Hook

// useLogin.ts
export const useLogin = () => {
  const mutation = useMutation({
    mutationFn: (input: LoginWithEmailPassword) => loginWithEmailPassword(input),
    // 可选:添加成功/失败回调
    onSuccess: (data) => {
      // 登录成功后的逻辑,比如存储token
    },
    onError: (error) => {
      // 处理登录错误
    }
  });

  return {
    login: mutation.mutate,
    data: mutation.data?.data,
    isLoading: mutation.isLoading,
    isSuccess: mutation.isSuccess,
    error: mutation.error
  };
};

// useLogout.ts
export const useLogout = () => {
  const mutation = useMutation({
    mutationFn: (accessToken: string) => logout(accessToken),
    onSuccess: () => {
      // 登出成功后的逻辑,比如清除本地token
    }
  });

  return {
    logout: mutation.mutate,
    isLoading: mutation.isLoading,
    isSuccess: mutation.isSuccess,
    error: mutation.error
  };
};

在AuthProvider中使用

// AuthProvider.tsx
const { login, data, isLoading, isSuccess: isLoginSuccess, error } = useLogin();
const { logout: logoutMutate, isLoading: isLogoutLoading } = useLogout();

const handleLogin = (input: LoginWithEmailPassword) => {
  login(input);
};

const handleLogout = () => {
  if (data?.accessToken) {
    logoutMutate(data.accessToken);
  }
};

这样就完全不需要维护loginInput和logoutInput状态,点击按钮直接调用login/logoutMutate即可触发操作,不会出现重复执行的问题。

2. 模拟useLazyQuery的效果(如果坚持用useQuery)

如果一定要用useQuery实现懒加载触发的效果,可以通过refetch方法手动触发查询,同时设置enabled: false让查询默认不执行:

修改useLogin Hook

export const useLogin = () => {
  const { data, isLoading, isSuccess, error, refetch } = useQuery(
    ["loginWithEmailPassword"],
    ({ queryKey, pageParam }) => loginWithEmailPassword(pageParam),
    {
      enabled: false, // 默认不执行
    }
  );

  // 封装带参数的触发函数
  const triggerLogin = (input: LoginWithEmailPassword) => {
    return refetch(input);
  };

  return { triggerLogin, data: data?.data, isLoading, isSuccess, error };
};

在AuthProvider中使用

const { triggerLogin, data, isLoading, isSuccess: isLoginSuccess } = useLogin();

const handleLogin = (input: LoginWithEmailPassword) => {
  triggerLogin(input);
};

这种方式下,查询只会在调用triggerLogin时执行,不会随组件重渲染重复运行。

3. 优化你的临时方案

如果暂时不想修改Hook,也可以优化你原来的重置逻辑,确保只在登录成功后重置状态,避免不必要的重渲染:

useEffect(() => {
  // 确保登录成功且数据存在时才重置
  if (isLoginSuccess && !isLoading && data) {
    setLoginInput(undefined);
  }
}, [isLoginSuccess, isLoading, data]);

注意要把isLoading和data也加入依赖数组,避免逻辑触发时机错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 18:36:24