已开启
[Bug][br_rnoh0.61] 关闭模态时崩溃 startAnimation 空引用及 hideModalContentWhileAnimating/panResponderThreshold/supportedOrientations 属性不生效 #25
mazheng创建于  13 天前
mazheng
13 天前 创建

本 issue 记录鸿蒙(RNOH)环境验证发现的缺陷,适用于 br_rnoh0.61 分支;其余分支见对应 issue。修复建议见文末。

问题描述

在鸿蒙(RNOH)环境验证 react-native-modal 时发现 4 个问题:1 个必现关闭崩溃(I 级)和 3 个 props 不生效,逐项描述如下。


问题一:关闭模态时必现报错页 typeerror: cannot read property 'startAnimation' of null

复现步骤

  1. 渲染一个基础模态 <Modal isVisible={visible}>
  2. 打开模态(一切正常,onModalShow 正常触发);
  3. 关闭模态(setVisible(false),含点击遮罩、onBackButtonPress 等任意关闭路径);
  4. 观察:弹出报错页面 typeerror: cannot read property 'startAnimation' of null

补充:在模态内通过按钮切换 isVisible 为 false、或快速连续切换可见性,均走同一关闭链,同样崩溃。

预期结果:模态正常关闭,依次触发 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,与双端语义有出入,可一并排查)。


问题二:hideModalContentWhileAnimating 属性不生效(死 prop)

复现步骤

  1. 分别渲染两个模态:A 设置 hideModalContentWhileAnimating={true},B 设置 false,其余配置相同;
  2. 打开 A、B 对比动画过渡期间内容的可见时机。

预期结果true 时整个开/关动画过渡期内模态内容以占位形式隐藏(上游语义:动画期内容不渲染);false 时内容随动画立即可见。

实际结果:A、B 打开后的表现完全一致,该属性取值无任何作用。

问题定位(供参考)src/modal.tsx 中该 prop 仅存在于 defaultPropspropTypes 声明,render 解构中未消费;shouldHideContent 的计算逻辑(为修复鸿蒙打开闪屏而引入的改造)未引用该 prop,属性被完全架空。


问题三:panResponderThreshold 属性不生效,手指按下即触发 onSwipeStart

复现步骤

  1. 渲染滑动关闭模态:swipeDirection="down",分别以 panResponderThreshold={4}panResponderThreshold={100} 对比;
  2. 手指刚按下(位移为 0)即观察 onSwipeStart 回调。

预期结果:滑动位移量小于阈值时不应识别为滑动手势,不应触发 onSwipeStart;阈值越大识别越"迟钝"(4 与 100 灵敏度应有明显差异)。

实际结果:手指按下的瞬间(dy=0)即触发 onSwipeStart,阈值 4 与 100 表现完全相同。

问题定位(供参考)src/modal.tsxonStartShouldSetPanResponder 无条件 return true 并立即触发 onSwipeStart(位移为 0 的假"识别"),onMoveShouldSetPanResponder 的阈值协商路径永不参与,导致阈值对识别灵敏度零影响(该问题继承自上游 13.0.1)。


问题四:supportedOrientations 属性在鸿蒙端不生效

复现步骤

  1. 渲染模态 A:supportedOrientations={['portrait']}(仅竖屏),模态 B:supportedOrientations=['landscape']
  2. 模态打开后旋转设备至横屏/竖屏。

预期结果:模态展示期间仅允许朝向集合内的方向展示(iOS UIModalPresentation 语义),portrait-only 的模态在横屏下不切换为横向布局。

实际结果:A、B 均可任意横竖屏旋转展示,朝向集合无任何约束作用。

问题定位(供参考):该 prop 经 RN Modal 的透传通道传递,但 RNOH 侧 RNModalHostView.etsModalHostViewProps 仅声明 transparent/visible/identifier 三个字段,supportedOrientations 在 ArkTS/C++ 侧无任何消费点。注:该 prop 属 iOS-only,Android 上游本就忽略;鸿蒙端可在库内以布局约束仿真(如窗口旋转至集合外朝向时按宽高互换回退到受支持朝向的几何)。


复现概率

  • 问题一:必现(100%,所有关闭模态的路径)
  • 问题二 / 三 / 四:必现(100%,对应属性差分场景)

环境信息

  • 框架:React Native 0.72(RNOH 0.72.x)
  • 库版本:@react-native-ohos/react-native-modal@13.0.3-rc.1(缺陷验证版本,基于上游 react-native-modal 13.0.1 适配)
  • 系统:HarmonyOS API 12+,真机验证

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 堆栈。

影响范围

  • 问题一:所有模态关闭/切换可见性的场景(含滑动关闭、返回键关闭、遮罩点击关闭、快速连续切换);
  • 问题二:依赖动画期间内容隐藏控制的场景(闪屏敏感业务);
  • 问题三:滑动关闭手势灵敏度配置场景;
  • 问题四:需要朝向约束的展示类业务(视频、全屏弹层等)。

修复建议

  1. 问题一:优先以 hilog 堆栈定界;若崩溃发生在库 close 链与 RNOH Modal 卸载时序交叠处,建议 finalizeClose 对动画实例增加判空守卫,并将 onModalHide 触发推迟到动画完成/卸载完成后;
  2. 问题二:shouldHideContent 恢复消费 hideModalContentWhileAnimatingtrue 恢复上游"整个过渡期以 opacity:0 占位隐藏内容"语义;false 保留现有针对打开闪屏的守卫逻辑),使该 prop 恢复为有效契约;
  3. 问题三:onStartShouldSetPanResponder 移除触摸按下即触发的 onSwipeStart(dy=0 假识别);onPanResponderMove 在首次方向锁定前增加阈值门限(|dx| 与 |dy| 均小于阈值时手势不识别、不派发 onSwipeStart/onSwipeMove),越过阈值才触发 onSwipeStart 并锁定滑动方向;onMoveShouldSetPanResponder 滚动协商路径保持上游行为;
  4. 问题四:RNOH 原生侧暂无朝向约束接口,可在库内以布局约束仿真 iOS 语义:窗口旋转至集合外朝向时按宽高互换回退到受支持朝向几何(portrait-upside-down / landscape-left / landscape-right 按宽高比归并判定),接入窗口尺寸变化回调与 open() 两条路径;默认 ['portrait','landscape'] 行为不变,iOS/Android 不受影响。
likedislike
Mmazheng
13 天前 关联了pull request:fix: 补充close/open链卸载守卫与兜底;panresponderThreshold阈值生效;supportedOrientations朝向约束