React Native接收SSE流数据时state未自动更新问题求助
问题根本原因
1. 闭包捕获了陈旧变量值
你将RNEventSource实例声明在了组件外部,startListenStatus中添加的消息回调函数,捕获的是组件首次渲染时的user、updateStatus等变量的引用。当用户从页面2跳转回页面1、组件发生重渲染后,回调函数里拿到的还是第一次渲染时的旧值:比如此时user.accountID已经更新为当前用户的真实ID,但回调里的user还是初始的空值/旧值,自然parsedData.account_id === user.accountID的条件判断无法命中。
你硬刷新Expo应用后,所有变量重新初始化,回调捕获的就是最新的正确值,所以逻辑可以正常执行。
2. useEffect依赖项不全
你第一个初始化用的useEffect依赖设置为空数组,仅会在组件首次挂载时执行一次,如果后续route.params.eventListen发生变化也不会触发逻辑更新;监听accountStatus的useEffect也没有把startListenStatus、stopListenStatus加入依赖,容易出现逻辑异常。
3. 全局SSE实例复用异常
全局复用同一个SSE实例,如果上次页面卸载时没有完全清理连接和监听,下次进入页面时添加的新监听会和旧监听冲突,也会导致逻辑不生效。
修复方案
- 把SSE实例的创建移到组件内部,每次进入pending状态时新建连接,不要复用全局实例
- 用
useRef存储最新的user等变量,避免闭包陷阱 - 补全所有useEffect的依赖项,组件卸载时强制清理SSE连接和监听
核心修改后的代码示例
export default function AccountRegister({ navigation, getUser, user, route}) { const [ accountStatus, setAccountStatus ] = useState(); const sseRef = useRef(null); // 用ref存最新的user,避免闭包拿旧值 const userRef = useRef(user); useEffect(() => { userRef.current = user; }, [user]); const startListenStatus = () => { // 每次启动监听时新建SSE实例 const sse = new RNEventSource('Third party URL', { headers: { 'Accept': 'text/event-stream', 'Authorization' : 'token' } }); sseRef.current = sse; sse.addEventListener('message', (event) => { const parsedData = JSON.parse(event.data); console.log('listening..'); // 从ref里拿最新的user if (parsedData.account_id === userRef.current.accountID) { console.log('found account'); if (parsedData.status_to === 'ACTIVE') { updateStatus('ACTIVE'); } } }); } const stopListenStatus = () => { if (sseRef.current) { sseRef.current.removeAllListeners(); sseRef.current.close(); sseRef.current = null; } getUser(); navigation.setParams({eventListen: false}); console.log('stopped listen'); } // 补全依赖 useEffect(() => { if ((route.params && route.params.eventListen)){ updateStatus('PENDING'); } else { getStatus(); } },[route.params, getStatus, updateStatus]) useEffect(() => { if (accountStatus === 'PENDING') { startListenStatus(); } else if (accountStatus === 'ACTIVE') { stopListenStatus(); } // 组件卸载时清理连接避免内存泄漏 return () => { if (sseRef.current) { sseRef.current.removeAllListeners(); sseRef.current.close(); } } },[accountStatus, startListenStatus, stopListenStatus]) // updateStatus、getStatus、渲染逻辑和原有代码保持一致即可 // ... }
内容的提问来源于stack exchange,提问作者Joon Joonam
相关产品推荐
相关产品推荐

