本 issue 记录鸿蒙(RNOH)环境验证发现的缺陷,适用于 br_rnoh0.61 分支;其余分支见对应 issue。修复建议见文末。
在鸿蒙(RNOH)环境验证 react-native-modal 时发现 4 个问题:1 个必现关闭崩溃(I 级)和 3 个 props 不生效,逐项描述如下。
react-native-modal
typeerror: cannot read property 'startAnimation' of null
复现步骤
<Modal isVisible={visible}>
onModalShow
setVisible(false)
onBackButtonPress
补充:在模态内通过按钮切换 isVisible 为 false、或快速连续切换可见性,均走同一关闭链,同样崩溃。
isVisible
预期结果:模态正常关闭,依次触发 onModalWillHide / onModalHide,无报错。
onModalWillHide
onModalHide
实际结果:关闭必现 typeerror 报错页;快速切换场景下模态"生效一瞬间"后即崩。
问题定位(供参考):库侧 close() → finalizeClose()(src/modal.tsx)链路中包含 stopAnimation 调用与关闭后延迟卸载逻辑;库侧 close 链内属性读取均有判空守卫,demo 处理器亦安全,崩溃点疑在 RNOH 侧 Modal visible=false 触发卸载与库 finalizeClose 的 setState / onModalHide 触发时序交叠。建议以 hilog 抓栈定界:栈落 modal.js 判库缺陷;落 React renderer / RNOH 判框架侧缺陷(另注意到 RNOH 侧 RNModalHostView.ets 在正常关闭路径也会 emit onRequestClose,与双端语义有出入,可一并排查)。
close()
finalizeClose()
src/modal.tsx
stopAnimation
visible=false
finalizeClose
RNModalHostView.ets
onRequestClose
hideModalContentWhileAnimating
hideModalContentWhileAnimating={true}
false
预期结果:true 时整个开/关动画过渡期内模态内容以占位形式隐藏(上游语义:动画期内容不渲染);false 时内容随动画立即可见。
true
实际结果:A、B 打开后的表现完全一致,该属性取值无任何作用。
问题定位(供参考):src/modal.tsx 中该 prop 仅存在于 defaultProps 与 propTypes 声明,render 解构中未消费;shouldHideContent 的计算逻辑(为修复鸿蒙打开闪屏而引入的改造)未引用该 prop,属性被完全架空。
defaultProps
propTypes
shouldHideContent
panResponderThreshold
onSwipeStart
swipeDirection="down"
panResponderThreshold={4}
panResponderThreshold={100}
预期结果:滑动位移量小于阈值时不应识别为滑动手势,不应触发 onSwipeStart;阈值越大识别越"迟钝"(4 与 100 灵敏度应有明显差异)。
实际结果:手指按下的瞬间(dy=0)即触发 onSwipeStart,阈值 4 与 100 表现完全相同。
问题定位(供参考):src/modal.tsx 的 onStartShouldSetPanResponder 无条件 return true 并立即触发 onSwipeStart(位移为 0 的假"识别"),onMoveShouldSetPanResponder 的阈值协商路径永不参与,导致阈值对识别灵敏度零影响(该问题继承自上游 13.0.1)。
onStartShouldSetPanResponder
return true
onMoveShouldSetPanResponder
supportedOrientations
supportedOrientations={['portrait']}
supportedOrientations=['landscape']
预期结果:模态展示期间仅允许朝向集合内的方向展示(iOS UIModalPresentation 语义),portrait-only 的模态在横屏下不切换为横向布局。
实际结果:A、B 均可任意横竖屏旋转展示,朝向集合无任何约束作用。
问题定位(供参考):该 prop 经 RN Modal 的透传通道传递,但 RNOH 侧 RNModalHostView.ets 的 ModalHostViewProps 仅声明 transparent/visible/identifier 三个字段,supportedOrientations 在 ArkTS/C++ 侧无任何消费点。注:该 prop 属 iOS-only,Android 上游本就忽略;鸿蒙端可在库内以布局约束仿真(如窗口旋转至集合外朝向时按宽高互换回退到受支持朝向的几何)。
Modal
ModalHostViewProps
transparent/visible/identifier
@react-native-ohos/react-native-modal@13.0.3-rc.1
// 问题一:关闭崩溃 const [visible, setVisible] = useState(false); <> <Button title="打开模态" onPress={() => setVisible(true)} /> <Modal isVisible={visible} onBackdropPress={() => setVisible(false)}> <View style={{ backgroundColor: '#fff', padding: 20 }}> <Text>Modal content</Text> </View> </Modal> </> // 打开正常、onModalShow 触发;关闭即报 // typeerror: cannot read property 'startAnimation' of null // 问题二:hideModalContentWhileAnimating 无差分 <Modal isVisible={v} hideModalContentWhileAnimating={true} animationIn="slideInUp" animationOut="slideOutDown"> ... </Modal> // 与 false 相比,动画期间内容可见时机无差异 // 问题三:panResponderThreshold 无差分 <Modal isVisible={v} swipeDirection="down" panResponderThreshold={100} onSwipeStart={() => console.log('swipe start')}> ... </Modal> // 手指按下(位移 0)即打印 swipe start,4 与 100 无区别 // 问题四:supportedOrientations 无约束 <Modal isVisible={v} supportedOrientations={['portrait']}> ... </Modal> // 设备旋转至横屏,模态仍横屏展示
问题一报错页截图:typeerror: cannot read property 'startAnimation' of null;如有需要可补充 hilog 堆栈。
onPanResponderMove
onSwipeMove
open()
['portrait','landscape']
问题描述
在鸿蒙(RNOH)环境验证
react-native-modal时发现 4 个问题:1 个必现关闭崩溃(I 级)和 3 个 props 不生效,逐项描述如下。问题一:关闭模态时必现报错页
typeerror: cannot read property 'startAnimation' of null复现步骤
<Modal isVisible={visible}>;onModalShow正常触发);setVisible(false),含点击遮罩、onBackButtonPress等任意关闭路径);typeerror: cannot read property 'startAnimation' of null。补充:在模态内通过按钮切换
isVisible为 false、或快速连续切换可见性,均走同一关闭链,同样崩溃。预期结果:模态正常关闭,依次触发
onModalWillHide/onModalHide,无报错。实际结果:关闭必现 typeerror 报错页;快速切换场景下模态"生效一瞬间"后即崩。
问题定位(供参考):库侧
close()→finalizeClose()(src/modal.tsx)链路中包含stopAnimation调用与关闭后延迟卸载逻辑;库侧 close 链内属性读取均有判空守卫,demo 处理器亦安全,崩溃点疑在 RNOH 侧 Modalvisible=false触发卸载与库finalizeClose的 setState /onModalHide触发时序交叠。建议以 hilog 抓栈定界:栈落 modal.js 判库缺陷;落 React renderer / RNOH 判框架侧缺陷(另注意到 RNOH 侧RNModalHostView.ets在正常关闭路径也会 emitonRequestClose,与双端语义有出入,可一并排查)。问题二:
hideModalContentWhileAnimating属性不生效(死 prop)复现步骤
hideModalContentWhileAnimating={true},B 设置false,其余配置相同;预期结果:
true时整个开/关动画过渡期内模态内容以占位形式隐藏(上游语义:动画期内容不渲染);false时内容随动画立即可见。实际结果:A、B 打开后的表现完全一致,该属性取值无任何作用。
问题定位(供参考):
src/modal.tsx中该 prop 仅存在于defaultProps与propTypes声明,render 解构中未消费;shouldHideContent的计算逻辑(为修复鸿蒙打开闪屏而引入的改造)未引用该 prop,属性被完全架空。问题三:
panResponderThreshold属性不生效,手指按下即触发onSwipeStart复现步骤
swipeDirection="down",分别以panResponderThreshold={4}和panResponderThreshold={100}对比;onSwipeStart回调。预期结果:滑动位移量小于阈值时不应识别为滑动手势,不应触发
onSwipeStart;阈值越大识别越"迟钝"(4 与 100 灵敏度应有明显差异)。实际结果:手指按下的瞬间(dy=0)即触发
onSwipeStart,阈值 4 与 100 表现完全相同。问题定位(供参考):
src/modal.tsx的onStartShouldSetPanResponder无条件return true并立即触发onSwipeStart(位移为 0 的假"识别"),onMoveShouldSetPanResponder的阈值协商路径永不参与,导致阈值对识别灵敏度零影响(该问题继承自上游 13.0.1)。问题四:
supportedOrientations属性在鸿蒙端不生效复现步骤
supportedOrientations={['portrait']}(仅竖屏),模态 B:supportedOrientations=['landscape'];预期结果:模态展示期间仅允许朝向集合内的方向展示(iOS UIModalPresentation 语义),portrait-only 的模态在横屏下不切换为横向布局。
实际结果:A、B 均可任意横竖屏旋转展示,朝向集合无任何约束作用。
问题定位(供参考):该 prop 经 RN
Modal的透传通道传递,但 RNOH 侧RNModalHostView.ets的ModalHostViewProps仅声明transparent/visible/identifier三个字段,supportedOrientations在 ArkTS/C++ 侧无任何消费点。注:该 prop 属 iOS-only,Android 上游本就忽略;鸿蒙端可在库内以布局约束仿真(如窗口旋转至集合外朝向时按宽高互换回退到受支持朝向的几何)。复现概率
环境信息
@react-native-ohos/react-native-modal@13.0.3-rc.1(缺陷验证版本,基于上游 react-native-modal 13.0.1 适配)DEMO
// 问题一:关闭崩溃 const [visible, setVisible] = useState(false); <> <Button title="打开模态" onPress={() => setVisible(true)} /> <Modal isVisible={visible} onBackdropPress={() => setVisible(false)}> <View style={{ backgroundColor: '#fff', padding: 20 }}> <Text>Modal content</Text> </View> </Modal> </> // 打开正常、onModalShow 触发;关闭即报 // typeerror: cannot read property 'startAnimation' of null // 问题二:hideModalContentWhileAnimating 无差分 <Modal isVisible={v} hideModalContentWhileAnimating={true} animationIn="slideInUp" animationOut="slideOutDown"> ... </Modal> // 与 false 相比,动画期间内容可见时机无差异 // 问题三:panResponderThreshold 无差分 <Modal isVisible={v} swipeDirection="down" panResponderThreshold={100} onSwipeStart={() => console.log('swipe start')}> ... </Modal> // 手指按下(位移 0)即打印 swipe start,4 与 100 无区别 // 问题四:supportedOrientations 无约束 <Modal isVisible={v} supportedOrientations={['portrait']}> ... </Modal> // 设备旋转至横屏,模态仍横屏展示日志/截图
问题一报错页截图:
typeerror: cannot read property 'startAnimation' of null;如有需要可补充 hilog 堆栈。影响范围
修复建议
finalizeClose对动画实例增加判空守卫,并将onModalHide触发推迟到动画完成/卸载完成后;shouldHideContent恢复消费hideModalContentWhileAnimating(true恢复上游"整个过渡期以 opacity:0 占位隐藏内容"语义;false保留现有针对打开闪屏的守卫逻辑),使该 prop 恢复为有效契约;onStartShouldSetPanResponder移除触摸按下即触发的onSwipeStart(dy=0 假识别);onPanResponderMove在首次方向锁定前增加阈值门限(|dx| 与 |dy| 均小于阈值时手势不识别、不派发onSwipeStart/onSwipeMove),越过阈值才触发onSwipeStart并锁定滑动方向;onMoveShouldSetPanResponder滚动协商路径保持上游行为;open()两条路径;默认['portrait','landscape']行为不变,iOS/Android 不受影响。