React Native中Context值变更时useEffect重复执行两次问题求助
1. Context Provider中状态更新时机错误
你在AuthContext的组件函数体中直接执行setPendingLinkData,每次Provider组件重渲染时,这个判断逻辑都会重新执行。如果signInMethods的条件多次满足(比如组件因其他原因重渲染),就会重复设置状态,导致pendingLinkData多次变更,触发useEffect多次执行。
另外,Provider的value是每次渲染都创建新对象,哪怕pendingLinkData没变化,也会导致所有使用useAuth()的组件强制重渲染,间接引发useEffect重复执行。
2. React Strict Mode的开发环境特性
在开发模式下,React Strict Mode会刻意重复调用useEffect、状态更新函数等,用来检测代码中的潜在问题(比如内存泄漏、非幂等操作)。这会导致你的useEffect在开发环境下执行两次,弹窗重复显示。
3. 组件重渲染导致的重复触发
如果消费Context的组件因父组件更新、自身props变化等原因多次重渲染,而pendingLinkData的引用在每次更新时被重新创建(比如对象未被memo化),也会被useEffect的依赖数组判定为变更,触发执行。
1. 修正Context中的状态更新逻辑
把状态更新逻辑放到useEffect中,避免在组件函数体直接调用setState,同时对Provider的value进行memo化:
// authContext.js import { useState, useEffect, useMemo } from 'react'; // ...其他代码 const [pendingLinkData, setPendingLinkData] = useState(null); // 把状态更新放到useEffect中,仅在依赖项变化时执行 useEffect(() => { if (signInMethods.includes("password") && !signInMethods.includes("google.com")) { setPendingLinkData({ email: userEmail, googleCredential }); } }, [signInMethods, userEmail, googleCredential]); // 对Provider的value进行memo化,避免不必要的组件重渲染 const contextValue = useMemo(() => ({ pendingLinkData, setPendingLinkData, }), [pendingLinkData]); return ( <AuthContext.Provider value={contextValue}> {children} </AuthContext.Provider> );
2. 给useEffect添加幂等守卫(避免重复弹窗)
用useRef记录是否已经处理过当前的pendingLinkData,确保同一个状态值只触发一次Alert:
// 消费组件中 const { pendingLinkData, setPendingLinkData } = useAuth(); const hasShownAlert = useRef(false); useEffect(() => { if (!pendingLinkData) { hasShownAlert.current = false; // 状态重置时清空标记 return; } // 如果已经显示过对应弹窗,直接返回 if (hasShownAlert.current) return; Alert.alert( "Account Exists", "An account with this email already exists. Do you want to link your Google account?", [ { text: "Cancel", style: "cancel", onPress: () => { setPendingLinkData(null); } }, { text: "OK", onPress: () => { setModalVisible(true); } } ] ); hasShownAlert.current = true; // 标记已显示 }, [pendingLinkData]);
3. 了解Strict Mode的影响
如果是开发环境下的重复弹窗,这是Strict Mode的正常行为。生产环境中React会自动关闭这个特性,不会出现重复执行的情况。如果想在开发时临时验证,可以暂时移除StrictMode包裹,但不建议长期这么做,因为它能帮你提前发现问题。
内容的提问来源于stack exchange,提问作者metrallador10

