如何防止AWS Amplify Auth事件重复触发导致后端多次调用?
解决AWS Amplify Auth事件重复触发导致后端多次调用的问题
你的问题核心在于Auth事件触发速度远快于eventCancelToken()的执行时机——当多个signIn/cognitoHostedUI事件同时触发时,多个getUserAndProcess已经进入异步流程,此时取消监听已经拦不住正在执行的请求了。下面是几个可行的解决思路:
思路1:添加执行锁,避免并发调用
在组件内维护一个状态标记是否正在处理用户,只有当标记为false时才执行逻辑,从根源上阻止重复调用:
// 组件内新增状态 const [isProcessing, setIsProcessing] = useState(false); const getUserAndProcess = async (eventCancelToken: () => void) => { // 如果正在处理,直接返回 if (isProcessing) return; setIsProcessing(true); try { console.log("processing user..."); const currentUser = await Auth.currentAuthenticatedUser() as CognitoUser; if(currentUser != null) eventCancelToken(); setUser(currentUser); // 获取token逻辑 const userSession = currentUser.getSignInUserSession(); const idToken = userSession?.getIdToken().getJwtToken(); const accessToken = userSession?.getAccessToken().getJwtToken(); const refreshToken = userSession?.getRefreshToken().getToken(); if(idToken === undefined || accessToken === undefined || refreshToken === undefined) { throw new Error("idToken, accessToken, or refreshToken is undefined"); } // 调用后端 const processUserResult = await _userService.processUser( { IdToken: idToken, AccessToken: accessToken, RefreshToken: refreshToken, } as ProcessUser ); console.log(user); console.log(processUserResult); } catch(error) { console.warn(error); } finally { // 无论成功失败,重置处理标记 setIsProcessing(false); } };
同时在事件回调中也加入判断:
Hub.listen("auth", ({ payload: { event, data }}) => { switch (event) { case 'signIn': case 'cognitoHostedUI': if (!isProcessing) { getUserAndProcess(authListenerCancelToken).catch(console.error); } break; case "signOut": setUser(null); break; case 'signIn_failure': case 'cognitoHostedUI_failure': console.log('Sign in failure', data); break; } });
思路2:提前过滤重复事件
Amplify的Auth事件有时会因内部状态同步触发多次,你可以通过用户唯一标识(如sub或username)过滤重复事件:
// 组件内新增状态记录已处理的用户ID const [processedUserId, setProcessedUserId] = useState<string | null>(null); Hub.listen("auth", ({ payload: { event, data }}) => { switch (event) { case 'signIn': case 'cognitoHostedUI': const userId = data?.sub || data?.username; // 只有用户ID存在且未处理过时才执行逻辑 if (userId && userId !== processedUserId) { getUserAndProcess(authListenerCancelToken).catch(console.error); setProcessedUserId(userId); } break; case "signOut": setUser(null); setProcessedUserId(null); // 登出后重置标记 break; case 'signIn_failure': case 'cognitoHostedUI_failure': console.log('Sign in failure', data); break; } });
思路3:调整取消监听的时机
不要等到currentAuthenticatedUser返回后才取消监听,而是在事件回调的第一时间就检查是否已有认证用户,有则直接取消监听:
Hub.listen("auth", async ({ payload: { event, data }}) => { switch (event) { case 'signIn': case 'cognitoHostedUI': try { // 先检查是否已有认证用户 const existingUser = await Auth.currentAuthenticatedUser(); if (existingUser) { authListenerCancelToken(); return; } } catch (e) { // 无用户,继续执行逻辑 } getUserAndProcess(authListenerCancelToken).catch(console.error); break; // 其他case逻辑不变 } });
额外优化:合并初始调用与事件监听逻辑
你在useEffect中直接调用getUserAndProcess可能和事件触发逻辑重复,可调整为仅在无认证用户时添加监听:
useEffect(() => { let authListenerCancelToken: (() => void) | null = null; const initAuth = async () => { try { // 初始检查是否有用户 const currentUser = await Auth.currentAuthenticatedUser() as CognitoUser; setUser(currentUser); // 直接调用后端,无需监听事件 await _userService.processUser(/* 传入token参数 */); } catch (e) { // 无用户时添加事件监听 authListenerCancelToken = Hub.listen("auth", ({ payload: { event, data }}) => { // 事件处理逻辑 }); } }; initAuth().catch(console.error); // 组件卸载时取消监听 return () => authListenerCancelToken?.(); }, []);
内容的提问来源于stack exchange,提问作者FailedUnitTest
相关产品推荐
相关产品推荐

