React Native中rn-tourguide如何区分start/stop事件
问题背景
在React Native项目中使用rn-tourguide包实现新用户操作引导功能,包的基础使用规则如下:
- 入口文件App.js中,在根组件外层包裹
TourGuideProvider完成全局初始化 - 业务组件中,在需要添加引导点位的元素外层包裹
TourGuideZone即可标记引导点
现有代码实现
App.js 全局配置
<TourGuideProvider preventOutsideInteraction={true} {...{ borderRadius: 16, tooltipComponent: TooltipComponent, }}> <NavStack notif={notifBool} /> </TourGuideProvider>
自定义Tooltip组件
const TooltipComponent = ({ isFirstStep, isLastStep, handleNext, handlePrev, handleStop, currentStep, }) => ( <View> <Text>{'Welcome'}</Text> <View> <TouchableOpacity> <Text>{'skip'}</Text> </TouchableOpacity> {!isFirstStep && ( <TouchableOpacity onPress={() => handlePrev()}> <Text>{'prev'}</Text> </TouchableOpacity> )} {!isLastStep && ( <TouchableOpacity onPress={() => handleNext()}> <Text>{'next'}</Text> </TouchableOpacity> )} {isLastStep && ( <TouchableOpacity onPress={() => handleStop()}> <Text>{'finish'}</Text> </TouchableOpacity> )} </View> </View> );
业务页面Demo.js
const Demo = ({navigation}) => { const { canStart, // 标记引导是否可启动的布尔值 start, // 启动引导方法 stop, // 终止引导方法 eventEmitter, // 内置事件监听实例 getCurrentStep, tourKey, } = useTourGuideController(); useEffect(() => { if (canStart) { start(); } return () => { stop(); }; }, [canStart]); const handleOnStop = () => { console.log('do something after stop'); } useEffect(() => { eventEmitter.on('start', ()=> console.log('do something')); eventEmitter.on('stop', handleOnStop); return () => { eventEmitter.on('start', ()=> console.log('do something')); eventEmitter.off('stop', handleOnStop); }; }, [eventEmitter]); return( <View> <TourGuideZone tourKey={tourKey} zone={1} text={' hello'} /> </View> ) }
现存问题
自定义TooltipComponent时,包内置传入的handlePrev/handleNext/handleStop方法没有提供回调入参,业务侧只能通过eventEmitter监听start/stop事件执行业务逻辑。但实际运行时会出现两个事件同时触发的异常,且包本身没有提供事件区分字段。
问题排查与修复方案
1. 事件监听器清理逻辑错误
当前代码的事件监听effect清理函数中,对start事件错误调用了on方法重复注册监听器,而非off方法移除监听器,会导致组件重渲染时监听器重复叠加,事件被多次触发。
修正后的监听逻辑:
useEffect(() => { const handleStart = () => console.log('do something'); eventEmitter.on('start', handleStart); eventEmitter.on('stop', handleOnStop); return () => { // 清理阶段必须移除所有已注册的监听器 eventEmitter.off('start', handleStart); eventEmitter.off('stop', handleOnStop); }; }, [eventEmitter]);
2. 引导启停逻辑触发时机错误
第一个控制引导启动的useEffect中,清理函数无条件执行stop(),当canStart状态变化、组件重渲染时,会先执行清理逻辑触发stop事件,之后effect重新执行又调用start()触发start事件,两个事件在同一渲染周期内连续触发,就会出现看似同时触发的现象。
通过ref标记引导实际运行状态,过滤无效触发即可解决:
// 新增ref存储引导实际运行状态 const isTourRunning = useRef(false); useEffect(() => { if (canStart) { start(); isTourRunning.current = true; } return () => { // 仅当引导真的处于运行状态时,才执行stop逻辑 if (isTourRunning.current) { stop(); isTourRunning.current = false; } }; }, [canStart, start, stop]); // 事件回调中增加状态校验,过滤无效触发 const handleOnStop = () => { if (!isTourRunning.current) return; isTourRunning.current = false; console.log('do something after stop'); } const handleStart = () => { if (isTourRunning.current) return; isTourRunning.current = true; console.log('do something on start'); }
3. 自定义Tooltip组件逻辑遗漏
当前skip按钮没有绑定handleStop事件,用户点击跳过引导时不会触发stop事件,会导致状态标记和实际运行状态不一致,补充点击事件即可:
<TouchableOpacity onPress={() => handleStop()}> <Text>{'skip'}</Text> </TouchableOpacity>
内容的提问来源于stack exchange,提问作者Shoyeb Memon
相关产品推荐
相关产品推荐

