Expo+react-navigation环境下useFocusEffect内事件监听器不触发问题
解决方案
- 清理useCallback依赖数组
你当前的依赖数组中放入了count、secCount、isPageRefreshing等未在回调逻辑中使用的状态,这些状态变更时会触发useCallback重新执行,先执行上一次返回的清理函数移除所有监听器,再绑定新的监听器,极易出现监听器被意外清理的问题。请移除所有未在回调内使用的依赖,仅保留真正用到的外部变量。 - 修改监听器清理逻辑
当前使用removeAllListeners()会清空对应Emitter实例上所有绑定的事件,若其他组件也绑定了同实例的事件会被意外清理。改为接收每个addListener返回的卸载函数,清理时仅移除当前组件绑定的监听器即可,修改后的参考代码如下:
useFocusEffect( React.useCallback(() => { // 清空所有待传输的 pending 信息 OperationPending.emit("notPending"); // 存储各个监听器的卸载方法 const unsubRefresh = RefreshEventEmitter.addListener("refresh", () => { sendMqttCmd("getTime"); }); const unsubConnStatus = MqttConnection.addListener("status", () => { setIsConnected(getWSConnStatus()); }); const response = setTimeout(() => setNoResponse(true), 20000); console.log("Just before event.."); const unsubFreshData = MqttEvents.addListener("freshDataAvailable", () => { console.log("Executed event in getDevice"); try { if (getDeviceTime().hr !== undefined) { console.log("Executed in getDevic"); const mqttTime = getDeviceTime(); setDateTimeObj({ hourCount: Number(mqttTime.hr), minCount: Number(mqttTime.min), secCount: Number(mqttTime.sec), }); clearTimeout(response); setTimeout(() => setIsPageRefeshing(false), 500); } } catch (error) {} }); // 仅清理当前组件注册的内容 return () => { console.log("Also unsubscribed.."); clearTimeout(response); clearInterval(shouldRenderInterval); unsubRefresh(); unsubConnStatus(); unsubFreshData(); }; // 仅保留实际用到的依赖 }, [MqttEvents, OperationPending, RefreshEventEmitter]) );
- 验证useFocusEffect触发逻辑
在useCallback回调开头添加日志,确认页面聚焦时日志正常输出。如果日志没有打印,首先检查useFocusEffect是否正确从@react-navigation/native包导入,其次检查当前路由是否属于嵌套导航,确认父导航路由处于激活状态。 - 校验事件源触发逻辑
在对应Emitter的emit触发位置添加日志,确认事件确实正常发出,排除事件源侧没有触发的问题。
内容的提问来源于stack exchange,提问作者gaurav
相关产品推荐
相关产品推荐

