本文记录把react-native-raw-bottom-sheet底部弹出面板 / Bottom Sheet适配到 HarmonyOS 的完整过程。它做的事很专一从屏幕底部滑出一块指定高度的面板带半透明遮罩点遮罩关闭、可选支持下拉手势与顶部拖拽条。结论这是目前遇到的最干净的一个库 —— 零依赖、零 peer、九项老写法全空、函数组件、四组几何像素级精确命中Modal在 RNOH 上完全可用。只有一个小行为值得注意close()不幂等没打开时调用它也会触发onClose。★★★★★ 一、四组几何像素级命中height是布局属性dump 直接可量关键设计src/index.jsconst{height260,openDuration300,closeDuration200,useNativeDriverfalse,…}props;constanimatedHeightuseRef(newAnimated.Value(0)).current;// 打开Animated.timing(animatedHeight,{useNativeDriver,toValue:height,duration:openDuration}).start();// 关闭Animated.timing(animatedHeight,{useNativeDriver,toValue:0,duration:closeDuration}).start(cb);…Animated.View style{[styles.container,{height:animatedHeight},…]}而styles.container是container:{backgroundColor:#fff,width:100%,height:0,overflow:hidden}⇒面板高度 heightpropvp、宽度 100% 屏宽✓⇒而且useNativeDriver默认是false⇒ 高度变化【进布局】⇒ 布局 dump 直接可量✓✓⇒拖拽条draggableIcon: {width: 35, height: 5, borderRadius: 5, margin: 10}✓实测density 3屏 1320×2232对象参数公式预言实测布局 dumpS1 面板height{200}200vp × 3/100%600 × 1320px✅h600 w1320 y1632S2 面板height{300}300vp × 3/100%900 × 1320px✅h900 w1320 y1332拖拽条draggabledraggableIcon 35×535vp/5vp × 3105 × 15px✅105x15 y1662遮罩styles.wrapper flex:1全屏1320 × 2232px✅1320x2232⇒四组全部精确命中✓✓⇒ 顺带一个合理的现象关闭状态下也能量到1320×2232的全屏块—— 那是测试页自己的根Viewflex:1不是 Modal 的遮罩遮罩只在Modal visible时存在✓★★★★ 二、Modal在 RNOH 上完全可用本轮最值得验证的一点这个库的整棵子树都挂在 RN 的Modal里Modal testIDModaltransparent visible{modalVisible}onRequestClose{…}KeyboardAvoidingView behavior{Platform.OSios?padding:height}…TouchableOpacity style{styles.mask}…/{/* 遮罩 */}Animated.View style{[styles.container,{height:animatedHeight}]}…/Animated.View/KeyboardAvoidingView/Modal实测相位驱动相位动作dump 结果P0两个实例都关闭没有面板✓P1S1.open()面板 600×1320 拖拽条 105×15✓P2S1.close()面板消失✓P3S2.open()面板 900×1320✓P4S2.close()面板消失✓⇒Modal的transparent/visible开关、子树渲染、层级都正常✓✓⇒ 而且Modal里的节点坐标是屏幕坐标y1632正好在屏幕底部上方 600px⇒ 量起来很直接 ✓★★★★ 三、useNativeDriver写对了显式false且没有告警useNativeDriverfalse,// ★ 默认值就是 falseprops 里可覆盖… Animated.timing(animatedHeight,{useNativeDriver,toValue:height,duration:openDuration}).start();Animated.spring(pan,{toValue:{x:0,y:0},useNativeDriver}).start();Animated.event([null,{dy:pan.y}],{useNativeDriver})(e,gestureState);⇒5 处用到useNativeDriver的地方全部显式传值✓⇒实测LogBox 里【没有任何通知】✓✓⇒ 这与Animated未指定useNativeDriver会常驻一条通知的情况正好相反 ——这个库的处理方式是正面的样本✓⇒ 也正因为false高度动画才会进布局才能被 dump 量到✓★★★ 四、一个值得注意的行为close()不幂等consthandleSetVisiblevisible{if(visible){…}else{Animated.timing(animatedHeight,{useNativeDriver,toValue:0,duration:closeDuration}).start((){setModalVisible(false);pan.setValue({x:0,y:0});if(typeofonClosefunction){onClose();}// ★ 无条件调用});}};useImperativeHandle(ref,()({open:()handleSetVisible(true),close:()handleSetVisible(false),}));⇒close()分支里没有任何当前是否已打开的判断⇒在从未打开过的实例上调用close()也会走完动画并触发onClose✓实测P0 就是两个实例都关闭的相位而我的 P0 动作是对二者各调一次close() P0全部关闭dump 里不应有面板 EVENT | S1 onClose第 1 次 ← S1 从未打开过却触发了 onClose EVENT | S2 onClose ← 同上 … EVENT | S1 onOpen第 1 次 ← P1 EVENT | S1 onClose第 2 次 ← P2 EVENT | S2 onOpen ← P3 EVENT | S2 onClose ← P4⇒回调的次数会比你预期的多从没开过 ⇒ 不会有 close这个直觉不成立✓⇒实践建议在onClose里做收尾逻辑时自己记一个之前是否真的打开过的标志位别默认它只在真的关闭时触发 ✓★★★ 五、其余预检结论都很干净预检项结果依赖✅✅dependencies与peerDependencies皆空import 图只有reactreact-native老写法九项✅✅全部为空现代函数组件forwardRefuseImperativeHandleuseStateuseRefmeasure()/measureInWindow/onLayout/findNodeHandle/NativeModules✅全空⇒ 零环境坑平台分支✅仅 1 处且安全behavior{Platform.OS ios ? padding : height}⇒ 鸿蒙取heightuseNativeDriver✅显式false5 处全传⇒ 无告警手势✅ 用 RN 核心的PanResponderonStartShouldSetPanResponder: () draggable⇒ 纯 JS、无原生依赖scripts✅无prepare/postinstall契约反射NOTE | 默认导出的 typeof object PASS | ★ 默认导出是 forwardRef 结果typeof 为 object不是 function NOTE | $$typeof 的字符串 Symbol(react.forward_ref) PASS | ★ render 存在forwardRef 的渲染函数 PROBE | S1 ref 可得 true / typeof open function / typeof close function⇒forwardRef的结果typeof是object而不是function—— 如果你按组件必须是 function写断言这条会误判 ✓六、交付包质量与基线说明类别结果交付包内容index.js57 Bre-exportsrc/index.js5290 Bsrc/style.js603 Bindex.d.ts⇒ 源码可读 ✓spec.json⚠️缺upstreamCommitvalidation⚠️build-pass-device-pending⚠️ 声明的基线reactNative: 0.88.0rnoh: 0.84.4verification: evidence/batch50/…⇒两字段自相矛盾且既不是当前宿主0.84.1/0.84.3也不是权威 0.88 基线自带测试__tests__/RBSheet.test.js 鸿蒙契约测试契约测试只校验包名/版本/入口/中文说明关于基线本次验证在RN0.84.1 RNOH0.84.3上完成。这个库没有任何已移除的写法行为只依赖Modal/Animated/PanResponder/KeyboardAvoidingView⇒结论跨基线成立。七、已知限制与本次验证边界7.1 用这个库要知道的事项说明★★★close()不幂等没打开时调用也会走完动画并触发onClose⇒onClose里做收尾要自己记标志位★★★★ 面板尺寸就是heightprop高度 heightvp宽度 100%屏宽默认height260★useNativeDriver默认false所以高度动画进布局想改成原生驱动要自己确认驱动的是不是纯 transform/opacity★ 打开时机open()会先setModalVisible(true)Modal才挂载再跑高度动画onOpen在动画【之前】同步触发★ 关闭是动画完再卸载close()的onClose在动画结束回调里触发closeDuration默认 200ms★draggableIcon固定 35×5、margin 10可用customStyles.draggableIcon覆盖★ 拖拽默认不生效draggable默认falseonStartShouldSetPanResponder: () draggable★ 拖拽条与内容只能二选一挂手势dragOnContent决定panHandlers挂在内容上还是拖拽条上★ 一处平台分支KeyboardAvoidingView的behavioriOS 用padding其余含鸿蒙用height★customStyles只能覆盖 4 个槽位wrapper/container/draggableIcon另有mask未接7.2 本次验证的边界★★ 手动按钮TextonPress点击后没有产生任何事件⇒ 可能是 RNOH 上Text.onPress不生效也可能是我的点击未命中按钮 ⇒未验证★ 拖拽手势未验证draggable的渲染部分已验证拖拽条 105×15px 出现 ✓但下拉关闭/回弹需要真实手势未验证closeOnPressMask点遮罩关闭未验证closeOnPressBackAndroid 硬件返回键 —— 鸿蒙上本就无此键未验证dragOnContent未验证customStyles/customModalProps/customAvoidingViewProps的覆盖效果未验证openDuration/closeDuration的精确时序只知onClose在动画后触发未验证height默认值 260 的实际渲染我显式传了 200 / 300也未验证children默认View /未验证同时打开两个实例时的层叠行为未在RN 0.88.0/RNOH 0.84.4上复验交付包声明的基线未测其它屏幕宽度未做跨平台对照。未改动库代码。八、常见问题Q1它在 RN 0.84 上能跑吗能而且很干净。Modal正常、面板几何四组精确命中、onOpen/onClose回调正常、LogBox 零通知。Q2面板高度怎么控制就是heightprop单位 vpRBSheet ref{ref} height{200} / // ⇒ 面板 200vp 高density 3 ⇒ 600px、宽为屏宽Q3onClose好像被多调用了一次对 ——close()不幂等即使实例从未打开过调用close()也会走完closeDuration的动画并触发onClose。⇒ 在onClose里做收尾时自己维护一个是否真的打开过的标志位。Q4为什么我调的open()没反应检查ref有没有真的接上组件是forwardRef只暴露open()/close()两个方法也可以用 ref 的可取性做判据typeof ref.current?.open function。Q5怎么让底部出现拖拽条RBSheet draggable{true} … /拖拽条是draggableIcon35×5vp、圆角 5、margin 10density 3 ⇒ 105×15px实测一致 ✓。Q6下拉手势为什么不生效draggable默认false而且真正挂panHandlers的位置取决于dragOnContentdragOnContentfalse默认⇒ 手势挂在拖拽条上dragOnContenttrue⇒ 手势挂在内容容器上且要求gestureState.dy 0。另外useNativeDriver默认false⇒ 手势更新走 JS 侧。Q7为什么布局 dump 里能量到面板因为useNativeDriver默认false而它驱动的是height布局属性⇒ 高度变化会反映到布局上。⚠️ 反之如果某个库用transform或opacity做动效dump 就量不到。Q8想换成原生驱动可以吗可以传useNativeDriver{true}但要确认被驱动的属性支持原生驱动本库驱动height⇒不建议。小结react-native-raw-bottom-sheet是零依赖、零 peer、现代函数组件的底部面板库forwardRefuseImperativeHandle九项老写法全空设备侧表现完全正常四组几何像素级命中LogBox 零通知。最值得记下来的是这几条★★★★★ 四组几何全部精确命中。面板height200⇒600×1320px、height300⇒900×1320px、拖拽条 ⇒105×15px、遮罩 ⇒1320×2232px✓✓⇒ 关键在于height是布局属性 useNativeDriver为false⇒ 高度变化进布局、dump 可量✓★★★★Modal在 RNOH 上完全可用。transparent/visible开关、子树渲染、层级都正常而且Modal 内节点用屏幕坐标量起来很直接 ✓★★★★useNativeDriver显式传false的正面样本。5 处全传 ⇒LogBox 零通知✓ —— 与未指定useNativeDriver会常驻一条通知的情况正好相反 ✓★★★ 一个真实的行为发现close()不幂等。源码close分支里没有当前是否已打开的判断⇒ 在从未打开过的实例上调用close()也会触发onClose实测 P0 阶段两个实例都没开过却各触发了一次onClose⇒onClose里做收尾要自己记标志位✓★★★ 平台分支只 1 处且安全KeyboardAvoidingView的behavior用Platform.OS ios ? padding : height⇒鸿蒙自然落到height✓ ——二元平台判断在第四平台上天然安全✓★★ 契约反射要记得forwardRef的typeof是object。Symbol(react.forward_ref)render才是它的特征按组件必须是function写断言会误判 ✓★ 一次时序漂移的教训与补救自动时间轴采样时会漂我第一次漏掉了 900px 那一档补救办法是重启后在目标相位的窗口内密集采样本次采到两次P3都给出h900 w1320✓⚠️ 一处未验证要如实写页面上的手动按钮TextonPress点击后没有产生任何事件⇒可能是 RNOH 上Text.onPress不生效、也可能是我点击未命中 ⇒未验证不写成结论 ✓本篇用到的库项内容三方库react-native-raw-bottom-sheet上游3.0.0的鸿蒙适配版适配仓库https://atomgit.com/oh-react-native/react-native-raw-bottom-sheet适配 TAG3.0.0-ohos-1.0.0需要 HAR / 权限 / ohpm都不需要纯 JS手势用 RN 核心PanResponder库类型底部弹出面板Bottom Sheet运行时依赖✅✅无dependencies与peerDependencies皆空prepare/postinstall✅都没有许可证MIT上游仓库https://github.com/nysamnang/react-native-raw-bottom-sheet★1203宿主工程RNOH084Demo测试页rnAppKey RawBottomSheetTestApp# 从适配仓库安装零依赖npminstallgithttps://atomgit.com/oh-react-native/react-native-raw-bottom-sheet.git#3.0.0-ohos-1.0.0--ignore-scriptsimport RBSheet from react-native-raw-bottom-sheet; const ref useRef(null); RBSheet ref{ref} height{260} // 面板高度vp⇒ density 3 时 ×3 得像素 openDuration{300} closeDuration{200} closeOnPressMask // 点遮罩关闭默认 true closeOnPressBack{false} // Android 硬件返回键默认 false draggable{false} // 打开后顶部出现拖拽条35×5vp dragOnContent{false} // 手势挂在拖拽条还是内容上 useNativeDriver{false} // 默认 false ⇒ 高度动画进布局 customStyles{{container: {borderTopLeftRadius: 12, borderTopRightRadius: 12}}} onOpen{() console.log(打开了)} onClose{() console.log(关闭了 —— ⚠️ 未打开过时调用 close() 也会触发)} View style{{padding: 16}}Text面板内容/Text/View /RBSheet // ref.current.open(); // 打开 // ref.current.close(); // 关闭⚠️ 不幂等 // ⚠️ 面板尺寸 height vp高× 屏宽100% // ⚠️ onOpen 在高度动画【之前】同步触发onClose 在【动画结束后】触发 // ⚠️ close() 未做是否已打开判断 ⇒ onClose 可能被多触发 // ⚠️ 一处平台分支KeyboardAvoidingView 的 behavioriOSpadding、其余height# 换页启动测试页 —— force-stop 不能省hdc shell aa force-stop com.rnoh084.demo hdc shell aa start-bcom.rnoh084.demo-aEntryAbility--psrnAppKey RawBottomSheetTestApp判据的复现方式# ① 契约 / 回调逐条 PASS/FAIL 日志hdc shellhilog -x | grep rbs-test|Select-StringPASS|FAIL|SUMMARY|NOTE|PROBE|EVENT# ⇒ forwardReftypeofobject、$$typeofSymbol(react.forward_ref)# ⇒ S1 onOpen/onClose、S2 onOpen/onClose 的顺序# ② 几何面板高 height prop × 3宽 1320屏宽devecocli ui layout|Select-String\[(\d),(\d),(\d),(\d)\]# ⇒ S1height200→ h600 w1320S2height300→ h900 w1320# ⇒ 拖拽条 105×15遮罩 1320×2232# ③ LogBox本库【零通知】useNativeDriver 显式 falsedevecocli ui layout|Select-Stringlogbox环境搭建本手册的验证环境是React Native for OpenHarmonyRNOH。从零准备开发环境DevEco Studio、HarmonyOS SDK API 26、Node.js、ohpm、hvigorw、模拟器与hdc调试请按 CPF-RN 开发者指南的「环境初始化」操作CPF-RN 开发者指南 · 02-搭建准备 · 环境初始化https://atomgit.com/CPF-RN/docs/blob/main/开发者指南/02-搭建准备/环境初始化.md环境就绪后本次验证使用的具体版本组合见文末「验证环境」一节。验证环境项版本React Native0.84.1React19.2.3RNOHnpm / ohpmreact-native-oh/react-native-harmony/rnoh/react-native-openharmony0.84.3Node.js / npmv24.14.0/11.9.0DevEco Studio26.0.0.621HarmonyOS SDKAPI 2626.0.0.32设备HarmonyOS 7.0.0(26.0.0) Beta2 模拟器Pura X Viewohos-x641320×2232density 3本机状态栏高度117px 39vp宿主依赖✅无需补装零依赖宿主 HAP 产物entry-default-signed.hap本次构建assembleHap3 分 34 秒⇒ 一次成功打包与构建退出码都检查了验证规模几何 4 组像素级命中600/900×1320 面板、105×15 拖拽条、1320×2232 遮罩契约反射forwardRef特征 ref.open/close回调链路 5 个相位全对LogBox 零通知4 张截图互异⚠️基线说明交付包spec.json声明RN 0.88.0 RNOH 0.84.4两字段自相矛盾本次验证在RN 0.84.1 RNOH 0.84.3上完成。本库无任何已移除写法行为只依赖Modal/Animated/PanResponder⇒结论跨基线成立。相关社区与项目地址项目地址React Native 鸿蒙官方社区CPF-RNhttps://atomgit.com/CPF-RNRNOH 框架仓库https://atomgit.com/openharmony-RN/ohos_react_nativeRNOH 三方库使用文档https://atomgit.com/OpenHarmony-RN/usage-docsReact Native for OpenHarmony 组织https://atomgit.com/oh-react-nativeRN 三方库鸿蒙适配清单https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview欢迎加入 CPF-RN 鸿蒙社区https://atomgit.com/CPF-RNReact Native for OpenHarmony 组织https://atomgit.com/oh-react-nativeRN 三方库鸿蒙适配清单https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview