React Native for OpenHarmony 实战:三方库 react-native-picker-modal-view 的鸿蒙化适配指南
本文记录把 react-native-picker-modal-view(弹窗式列表选择器)适配到 HarmonyOS 的完整过程。
它做的事:渲染一个"选择框",点它弹出一个 Modal 列表(带搜索框、字母索引、滚动到底部的"回到顶部"按钮),选中后回调。
结论:可用。但本轮最重要的收获不是"能不能跑",而是一个交付包结构上的陷阱 ——
main 指向的 dist/index.js 只有 export default,而 src/index.ts 里有 20 多个具名导出。
⇒ 直接 import {AnimationTypeEnum, generateAlphabet} from 'react-native-picker-modal-view' 全部拿到 undefined,
调用 generateAlphabet() 直接抛 undefined is not a function。
修正方法:默认导出正常导入,具名导出改从 dist/ 的子路径导入。
修正之后:6 个具名组件全部可用、10 个公开方法全在 prototype 上、CommonStyle.BTN_HEIGHT 精确等于 50、
generateAlphabet 精确返回 ["A","B","C"]、getIndex("B") 精确返回 1、openModal() 状态机全程通过。
★★★★★ 一、最重要的发现:dist/index.js 只有默认导出
dist/index.js(131 字节,全文就这两行):
import { ModalComponent as PickerModal } from './Components/Modal';
export default PickerModal;
//# sourceMappingURL=index.js.map
而 src/index.ts 有完整的具名导出:
export { ModalStyles } from '@Styles/ModalStyles';
export { AnimationTypeEnum } from '@Enum/AnimationTypeEnum';
export const generateAlphabet = (items, sortingLanguage?) => { … }
export const getFilteredData = (items, autoSort, searchText) => { … }
export { ModalComponent } from '@Components/Modal';
export { AlphabetComponent } from '@Components/Alphabet';
…
export default PickerModal;
dist/index.d.ts 也声明了那些具名导出 ⇒ 于是 TypeScript 编译期一切正常 ✓
但运行时 dist/index.js 里没有它们 ✗
实测后果(第一次跑):
PASS | ★★ 默认导出是 class(function) ← 默认导出 ✓
PASS | ★ 有 prototype.render
FAIL | ★★ 默认导出 === 具名 ModalComponent | 期望=true | 实际=false
NOTE | 不是 class 的组件 = ModalComponent, AlphabetComponent, ListItemComponent,
SearchComponent, ScrollToTopComponent, SelectBoxComponent ← 6 个全 undefined
NOTE | AnimationTypeEnum 的键数 = 0
NOTE | ModalComponent.defaultProps 的键数 = 0
NOTE | CommonStyle.BTN_HEIGHT = undefined
NOTE | ModalStyles 的 typeof = undefined
===== B. 纯函数(4 个) ===== ← 然后就崩了
LogBox: "undefined is not a function" ← generateAlphabet() 调用抛错
⇒ ⇒ 这就是"类型正确、运行时缺失"的典型表现 ✓
修正后的导入方式(默认走入口,具名走子路径):
// ① 默认导出:正常从包入口导入
import PickerModal from 'react-native-picker-modal-view';
// ② 具名导出:必须从 dist 的子路径导入(这些路径已用 node 实测确认存在)
import {generateAlphabet, getFilteredData, getIndex, trCompare}
from 'react-native-picker-modal-view/dist/Helpers';
import {AnimationTypeEnum}
from 'react-native-picker-modal-view/dist/Enum/AnimationTypeEnum';
import {ModalComponent} from 'react-native-picker-modal-view/dist/Components/Modal';
import {CommonStyle} from 'react-native-picker-modal-view/dist/Assets/Styles/CommonStyle';
import {ModalStyles} from 'react-native-picker-modal-view/dist/Assets/Styles/ModalStyles';
注意样式目录名:是 dist/Assets/Styles/,不是 dist/Styles/(这个也要按实际目录来)✓
修正后的成绩:
PASS | ★★ 默认导出 === 具名 ModalComponent
PASS | ★★★ 6 个具名组件都是 class(有 prototype.render)
PASS | ★★★ CommonStyle.BTN_HEIGHT = 50
PASS | ★★ ModalStyles 是 object
PASS | ★★★ 10 个公开方法都在 prototype 上
LogBox: 不再有 undefined is not a function
这条教训很通用:不要假设"源码的导出 == 运行时的导出"。
判断依据很简单:看package.json的main指向哪个文件、再把这个文件打开看它导出了什么。
本轮main指向dist/index.js(131 B),它只有export default⇒ 20 多个具名导出在运行时不存在 ✓
★★★★★ 二、prototype 上的 10 个方法:这次真的是原型方法
.d.ts 里这 10 个公开方法的签名是 name(): void;(不是 name: () => …):
private _clearComponent() → public clearComponent(): void
private _openModal() → public openModal(): void
private _onClose() → public onClose(): void
private _onBackRequest() → public onBackRequest(): void
private _scrollToUp() → public scrollToUp(): void
private _onScrolling(e) → public onScrolling(e): void
private _renderItem(...) → public renderItem(...): JSX.Element
private _setText(text) → public setText(text): void
private _onSelectMethod(key)→ public onSelectMethod(key): …
private _setAlphabet(a) → public setAlphabet(a): void
源码里的实际写法(每个都是"公开方法转发给私有方法"):
private _openModal(): void { … }
public openModal(): void { this._openModal(); }
实测:
NOTE | prototype 上缺失的方法 = (无)
PASS | ★★★ 10 个公开方法都在 prototype 上 | 期望=0 | 实际=0
⇒ ⇒ 这次可以放心断言"在 prototype 上" ✓
⇒ 对照:有些库里那 6 个方法(setValue / getISOCode / …)是箭头类字段、只在实例上。
⇒ 判别方法不变:.d.ts 里 name(…): … 是原型方法、name: (…) => … 是实例字段 ✓
★★★★★ 三、精确值判据:三个都一次命中
① CommonStyle.BTN_HEIGHT = 50
源码写死:export const BTN_HEIGHT: number = 50;(它被用作 FlatList 的 getItemLayout 行高)
NOTE | CommonStyle.BTN_HEIGHT = 50
PASS | ★★★ CommonStyle.BTN_HEIGHT = 50
② generateAlphabet 精确返回 ["A","B","C"]
源码逻辑:取每个 Name 的首字母、去重、排序。我用 4 项数据:
[{Name:'Apple'}, {Name:'Banana'}, {Name:'Apricot'}, {Name:'Cherry'}]
// 首字母:A, B, A, C ⇒ 去重后 [A,B,C] ⇒ 排序后 ["A","B","C"]
NOTE | generateAlphabet(4 项) 的结果 = ["A","B","C"]
PASS | ★★★ generateAlphabet ⇒ [A,B,C](3 个)
③ getIndex("B") 精确返回 1
getIndex('B', items, false, '') // 找 Name 首字母 === 'B' 的索引 ⇒ Banana 在原数组第 2 位 ⇒ 1
NOTE | getIndex("B", items, false, "") = 1
PASS | ★★★ getIndex("B") ⇒ 1
PASS | ★★ getIndex("Z") ⇒ -1(不存在)
⇒ ⇒ 这三条都是"先算出真值、再断言同一个值" ✓
⇒ 特别是 generateAlphabet 与 getIndex:我按源码逻辑在纸上推了一遍结果,然后断言 —— 所以不是"验证自己" ✓
★★★★ 四、状态机全程通过:modalVisible 的 false → true
render() 里 <Modal visible={modalVisible}>,而 modalVisible 来自 state。
openModal() 只在 items.length > 0 && !disabled 时才置 true:
private _openModal(): void {
if (items.length > 0 && !disabled) {
this.setState({ modalVisible: true });
}
}
实测三相位:
NOTE | state 的键 = modalVisible, searchText, stickyBottomButton, selectedAlpha, selectedObject
NOTE | state.modalVisible(初始) = false
PASS | ★★★ 初始 state.modalVisible = false
PASS | ★★ ref 调用 openModal() 不抛错
NOTE | openModal 之后 state.modalVisible = true
PASS | ★★★ openModal() 之后 state.modalVisible = true
NOTE | setText 之后 state.searchText = ap
PASS | ★★★ setText("ap") 之后 state.searchText = "ap"
⇒ ⇒ openModal() 与 setText() 两个实例方法都真实改变了 state ✓(不是"调用成功但没生效")
选择框文本也拿到了坐标:
✓ 「PMV 点我选择」 w=269 h=49 y=1163
⇒ 这就是我传的 selectPlaceholderText,说明 SelectBoxComponent 正常渲染 ✓
★★★ 五、3 条没通过的(都记在这里)
① AnimationTypeEnum 仍是 undefined
dist/Enum/AnimationTypeEnum.js 只有 45 B,我从 dist/Enum/AnimationTypeEnum 导入仍然是 undefined。
⇒ 本轮未查明原因(可能是该文件的写法/导出形态在 Metro 下被静默吞掉)。
⇒ 不影响主功能:defaultProps.modalAnimationType 的默认值就是 'slide',说明枚举值本身是对的。
⇒ 记为"未验证" ✓
② defaultProps 实际 13 项,我断言 14 项(我又数错了)
NOTE | ModalComponent.defaultProps 的键数 = 13
NOTE | ModalComponent.defaultProps 的键 = autoGenerateAlphabeticalIndex, autoSort, disabled, items,
modalAnimationType, removeClippedSubviews, requireSelection, searchInputTextColor,
searchPlaceholderText, selectPlaceholderText, showAlphabeticalIndex, showToTopButton, sortingLanguage
FAIL | ★★★ defaultProps 恰好 14 项 | 期望=14 | 实际=13
⇒ ⇒ 这是连续第四轮在"数量类断言"上出错 ✗
⇒ 而这次的讽刺之处是:我已经把真值打成 NOTE 了(13),却仍然在断言里写了字面量 14 ✗
⇒ ⇒ 改法必须再收紧:数量断言的期望值【直接用同一个变量】:
const n = Object.keys(dp).length;
note('defaultProps 的键数', n);
check('defaultProps 恰好 N 项', n, n); // ✗ 这样永远 PASS,没有意义
// 正确做法:把"从 .d.ts 数出来的期望值"也存成变量,两边都打印
const expectedCount = 13; // ← 从 .d.ts 逐项数,并在注释里列出这 13 个键
check('defaultProps 恰好 ' + expectedCount + ' 项', expectedCount, n);
⇒ ⇒ 更实用的做法:断言"键名清单"而不是"数量" ——
把 .d.ts 里那 13 个键名直接列成数组比对,数量错、名字错都会被抓到 ✓
③ getFilteredData(items, false, 'ap') 返回 0 项(与源码逻辑不符)
源码逻辑(src/Helpers/index.ts L46-51):
export const getFilteredData = (items, autoSort, searchText) => {
if (autoSort) { items.sort((a, b) => trCompare(a.Name, b.Name)); }
return items.filter((l) => l.Name.toLocaleLowerCase().indexOf(searchText.toLocaleLowerCase()) > -1);
};
按这个逻辑,'Apple'.toLocaleLowerCase() = 'apple'、'ap'.toLocaleLowerCase() = 'ap'、
'apple'.indexOf('ap') = 0 > -1 ⇒ 应该匹配 ✓ 但实测返回 0 项:
NOTE | getFilteredData(items, false, "ap") 的结果 = []
FAIL | ★★★ 过滤 "ap" ⇒ 2 项(Apple/Apricot) | 期望=2 | 实际=0
PASS | ★★ 过滤空串 ⇒ 4 项(全部) ← 说明 items 本身没问题
PASS | ★★ 过滤 "zzz" ⇒ 0 项
FAIL | ★★★ 大小写不敏感("AP" 也是 2 项) | 期望=2 | 实际=0
⇒ ⇒ 空串能匹配 4 项(说明 items 与 Name 都正常)、'zzz' 匹配 0 项(正常),但 'ap' 也匹配 0 项 ✗
⇒ 本轮未查明原因(可能与 toLocaleLowerCase() 在 Hermes 上的行为有关,也可能是我传入的数据在设备侧有差异)
⇒ 如实记为"未解释的偏差" ✓ —— 不写成"库有问题",因为我没有定位到根因
★★ 六、其余预检结论
| 预检项 | 结果 |
|---|---|
老写法九项 + ListView + ViewPropTypes + Text.propTypes + measure + onLayout + useNativeDriver + Animated | ✅✅✅ 全部为空 |
dependencies | ✅ 空对象 {} |
peerDependencies | ✅ 只有 react / react-native ⇒ 无原生 peer |
Platform.OS 3 处 | behavior={Platform.OS === 'ios' ? 'padding' : null} ⇒ 鸿蒙取 null ✓ 安全 |
NativeModules 2 处 | ⚠️ 但都在注释里(// const STATUSBAR_HEIGHT = …)✓ |
<Modal> 1 处 / FlatList 24 处 | ✓(<Modal> 在鸿蒙上已验证可用) |
scripts.prepare | ✅ 空 |
| 交付包 | src/(TS 源码)+ dist/(编译产物)+ Assets/Images(图标 png) |




七、接入与预检(要点)
# dependencies 是空对象、peer 只有 react/react-native ⇒ 装上就能用
npm install "git+https://atomgit.com/oh-react-native/react-native-picker-modal-view.git#1.3.2-ohos-1.0.0" --ignore-scripts
八、交付包质量与基线说明
| 类别 | 结果 |
|---|---|
| 交付包内容 | src/(TS 源码,20+ 具名导出)+ dist/(编译产物,index.js 只 export default)+ Assets/Images(图标) |
| ⚠️ 结构问题 | main 指向的 dist/index.js 只有默认导出;具名导出只存在于 src/index.ts 与 dist/*.d.ts ⇒ 类型与运行时不一致 |
spec.json | ⚠️ 缺 upstreamCommit |
validation | ⚠️ "build-pass-device-pending" |
| ⚠️ 声明的基线 | reactNative: "0.88.0" + rnoh: "0.84.4" ⇒ 两字段自相矛盾 |
| 备注 | 库代码零改动 |
关于基线:本次验证在 RN 0.84.1 + RNOH 0.84.3 上完成。
本库无已移除写法、无原生依赖、Platform.OS 只有一处惰性分支 ⇒ 结论跨基线成立 ✓
九、已知限制与本次验证边界
9.1 用这个库要知道的
| 事项 | 说明 |
|---|---|
★★★★★ dist/index.js 只有默认导出 | 具名必须从 dist/ 子路径导入;样式在 dist/Assets/Styles/(不是 dist/Styles/) |
| ★★★★★ 具名导出清单(修正后可用) | 6 个组件:ModalComponent / AlphabetComponent / ListItemComponent / SearchComponent / ScrollToTopComponent / SelectBoxComponent |
★★★★★ 10 个公开方法都在 prototype 上 | clearComponent / openModal / onClose / onBackRequest / scrollToUp / onScrolling / renderItem / setText / onSelectMethod / setAlphabet |
★★★★★ CommonStyle.BTN_HEIGHT = 50 | 被用作 FlatList 的 getItemLayout 行高 |
★★★★ openModal() 只在 items.length > 0 && !disabled 时才弹 | 空数组或 disabled 时调它不会有反应(不报错) |
★★★ defaultProps 有 13 项 | 含 items(默认 [])、autoSort、requireSelection、selectPlaceholderText 等 |
| ★★★ 有 4 个纯函数可独立使用 | generateAlphabet / getFilteredData / getIndex / trCompare(dist/Helpers) |
★★ <Modal> + FlatList 承载列表 | 两者在鸿蒙上都验证过可用 |
★ setText() 会把搜索词写进 state | 列表数据来自 getFilteredData(items, autoSort, searchText) |
9.2 本次验证的边界
- ★★★ 未验证"点选择框弹出列表"的真实触摸路径(用
openModal()编程调用); - ★★★ 未验证列表项的点击选中(
onSelectMethod/onSelected回调未触发); - ★★★
Modal打开后列表项文本未在布局 dump 里定位到(只确认了选择框文本PMV 点我选择 y=1163); - ★★
AnimationTypeEnum仍是undefined(子路径导入失败,原因未查明); - ★★
getFilteredData('ap')返回 0 项(与源码逻辑不符,未查明); - ★★ 未验证字母索引栏(
AlphabetComponent)的点击跳转; - ★★ 未验证滚动到底部的"回到顶部"按钮(
ScrollToTopComponent); - 未验证
renderSelectView/renderListItem/renderSearch三个自定义渲染入口; - 未在
RN 0.88.0/RNOH 0.84.4上复验; - 未测其它屏幕宽度;未做跨平台对照。
库代码未改动。
十、常见问题
Q1:它在 RN 0.84 上能跑吗?
能。 默认导出是 class、10 个公开方法都在 prototype 上、CommonStyle.BTN_HEIGHT = 50、
generateAlphabet 精确返回 ["A","B","C"]、openModal() 把 modalVisible 从 false 置为 true、
选择框文本渲染在 y=1163;修正导入后 LogBox 无通知。
Q2:import {AnimationTypeEnum} from 'react-native-picker-modal-view' 拿到 undefined?
因为 main 指向的 dist/index.js 只有 export default PickerModal(131 B)。
.d.ts 里声明的那些具名导出在运行时并不存在 ⇒ 必须从子路径拿:
import PickerModal from 'react-native-picker-modal-view'; // ✓ 默认导出
import {AnimationTypeEnum}
from 'react-native-picker-modal-view/dist/Enum/AnimationTypeEnum'; // 具名走子路径
import {generateAlphabet} from 'react-native-picker-modal-view/dist/Helpers';
import {CommonStyle} from 'react-native-picker-modal-view/dist/Assets/Styles/CommonStyle';
⚠️ 样式目录是 dist/Assets/Styles/,不是 dist/Styles/。
Q3:调 openModal() 没反应?
因为它只在 items.length > 0 && !disabled 时才置 modalVisible: true:
if (items.length > 0 && !disabled) { this.setState({ modalVisible: true }); }
⇒ 先确认 items 不是空数组、disabled 不是 true。(不满足时不会报错,只是什么都不做)
Q4:defaultProps 有几项?
13 项:autoGenerateAlphabeticalIndex / autoSort / disabled / items / modalAnimationType /
removeClippedSubviews / requireSelection / searchInputTextColor / searchPlaceholderText /
selectPlaceholderText / showAlphabeticalIndex / showToTopButton / sortingLanguage。
Q5:怎么用它的纯函数?
import {generateAlphabet, getFilteredData, getIndex, trCompare}
from 'react-native-picker-modal-view/dist/Helpers';
const items = [{Name:'Apple'},{Name:'Banana'},{Name:'Apricot'},{Name:'Cherry'}];
generateAlphabet(items); // ["A","B","C"](首字母去重 + 排序)
getIndex('B', items, false, ''); // 1
trCompare('A','A'); // 0
⚠️ getFilteredData(items, autoSort, searchText) 的 autoSort 为 true 时会原地排序 items(副作用)。
Q6:AnimationTypeEnum 到底怎么拿?
本轮我从 dist/Enum/AnimationTypeEnum 导入仍然拿到 undefined(原因未查明)。
但它的值从 defaultProps.modalAnimationType 可以间接确认:默认是 'slide'。
所以实际用法上直接传字符串 'slide' / 'fade' / 'none' 即可:
<PickerModal modalAnimationType="slide" … />
小结
react-native-picker-modal-view 是弹窗式列表选择器(选择框 + Modal + FlatList + 搜索 + 字母索引 + 回到顶部),
dependencies 是空对象、peer 只有 react/react-native、老写法九项全空 —— 代码本身很干净。
最值得记下来的是这几条:
-
★★★★★ “源码的导出"不等于"运行时的导出”。
dist/index.js(main指向的)只有 131 B、只有export default;
而src/index.ts与dist/index.d.ts里有 20+ 具名导出 ⇒ TS 编译期正常、运行时全undefined✗
后果很典型:generateAlphabet()抛undefined is not a function、LogBox报错、后续判据全崩。
⇒ 检查方法很简单:打开package.json的main指向的那个文件,看它到底export了什么。
⇒ 修正方式:默认导出走入口,具名走dist/子路径(并注意样式在dist/Assets/Styles/)。 -
★★★★★ 精确值判据三条全部一次命中。
CommonStyle.BTN_HEIGHT = 50、generateAlphabet(4项) ⇒ ["A","B","C"]、getIndex("B") ⇒ 1
—— 都是先在设备外用源码逻辑推出真值、再到设备上断言同一个值 ✓ -
★★★★★ 成员归属再添一例(这次是"原型方法")。
10 个公开方法都是public x(): void { this._x(); }⇒ 全在prototype上、断言一次通过 ✓
⇒ 与某些库里那 6 个箭头类字段(只在实例上)正好构成对照 ⇒
判别依据始终是.d.ts的写法:name(…): …vsname: (…) => …✓ -
★★★★
openModal()的状态机可观测且全程通过:
初始modalVisible = false→ 调用 →true;setText('ap')→state.searchText = 'ap'✓
⇒ 这类"状态位移"是验证实例方法最直接的证据。 -
★★★ 我又在"数量类断言"上错了(连续第四轮)。
defaultProps实际 13 项、我写了 14 ✗ —— 而且我已经把真值 13 打成NOTE了,仍写错字面量。
⇒ 改法必须从"数量"改成"清单":把.d.ts里的键名逐个列成数组比对,
数量错、名字错都会被抓到,比断言一个数字可靠得多 ✓ -
★★★ 老实承认两条"未解释"的偏差:
AnimationTypeEnum仍undefined、getFilteredData('ap')返回 0 项(源码逻辑上应匹配 2 项)。
⇒ 没有查明根因,就不写成"库有 bug" —— 只写"实测与预期不符、原因未查明" ✓ -
★★
NativeModules命中要看上下文:2 处全在注释里(// const STATUSBAR_HEIGHT = …)⇒ 不是真实使用 ✓
本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-picker-modal-view(上游 1.3.2 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-picker-modal-view |
| 适配 TAG | 1.3.2-ohos-1.0.0 |
| 需要 HAR / 权限 / ohpm | 都不需要(纯 JS) |
| 库类型 | 弹窗式列表选择器(Modal + FlatList + 搜索 + 字母索引) |
| 运行时依赖 | ✅ dependencies 是空对象;peer 只有 react / react-native |
| 导出形态 | 默认导出 PickerModal(class);具名导出需从 dist/ 子路径拿(入口 dist/index.js 只有默认导出) |
| ⚠️ 结构陷阱 | 类型声明里有具名、运行时入口没有 ⇒ 必须用子路径 |
| 许可证 | MIT |
| 上游仓库 | https://github.com/pankod/react-native-picker-modal-view |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = PickerModalTestApp) |
npm install "git+https://atomgit.com/oh-react-native/react-native-picker-modal-view.git#1.3.2-ohos-1.0.0" --ignore-scripts
// ① 默认导出:正常从入口导入
import PickerModal from 'react-native-picker-modal-view';
// ② 具名导出:必须从 dist 子路径(入口只有 default)
import {generateAlphabet, getFilteredData, getIndex, trCompare}
from 'react-native-picker-modal-view/dist/Helpers';
import {CommonStyle} from 'react-native-picker-modal-view/dist/Assets/Styles/CommonStyle';
import {ModalComponent} from 'react-native-picker-modal-view/dist/Components/Modal';
const ITEMS = [
{Id: 1, Name: 'Apple', Value: 'apple'},
{Id: 2, Name: 'Banana', Value: 'banana'},
{Id: 3, Name: 'Apricot', Value: 'apricot'},
{Id: 4, Name: 'Cherry', Value: 'cherry'},
];
<PickerModal
items={ITEMS}
showAlphabeticalIndex
showToTopButton
selectPlaceholderText="请选择"
searchPlaceholderText="搜索"
modalAnimationType="slide"
onSelected={(item) => console.log(item.Name)}
onClosed={() => {}}
/>
// 实例方法(10 个都在 prototype 上)
const ref = useRef(null);
ref.current.openModal(); // ★ 只在 items.length > 0 && !disabled 时才弹
ref.current.setText('ap');
ref.current.clearComponent();
ref.current.onClose();
ref.current.scrollToUp();
ref.current.setAlphabet('B');
// ⚠️ dist/index.js 只有 export default ⇒ 具名必须走 dist 子路径
// ⚠️ 样式在 dist/Assets/Styles/(不是 dist/Styles/)
// ⚠️ CommonStyle.BTN_HEIGHT = 50(FlatList 行高)
// ⚠️ defaultProps 13 项(items 默认 []、modalAnimationType 默认 'slide')
// ⚠️ getFilteredData(items, true, text) 会原地排序 items(副作用)
# 换页启动测试页 —— force-stop 不能省
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey PickerModalTestApp
判据的复现方式
# ① 契约 + 纯函数 + 状态机(53 行)
hdc shell "hilog -x | grep pmv-test" | Select-String 'PASS|FAIL|SUMMARY|NOTE'
# ⇒ BTN_HEIGHT=50;generateAlphabet ⇒ ["A","B","C"];getIndex("B") ⇒ 1
# ⇒ modalVisible: false → openModal() → true
# ② 选择框文本
devecocli ui layout | Select-String 'PMV 点我选择'
验证环境
| 项 | 版本 |
|---|---|
| React Native | 0.84.1 |
| React | 19.2.3 |
| RNOH(npm / ohpm) | @react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3 |
| Node.js / npm | v24.14.0 / 11.9.0 |
| DevEco Studio | 26.0.0.621 |
| HarmonyOS SDK | API 26(26.0.0.32) |
| 设备 | HarmonyOS 7.0.0(26.0.0) Beta2 模拟器 Pura X View(ohos-x64),1320×2232,density 3 |
| 本机状态栏高度 | 117px = 39vp |
| 宿主依赖 | ✅ 无需额外依赖(dependencies 空) |
| 宿主 HAP 产物 | entry-default-signed.hap |
| 本次构建 | assembleHap 3 分 33 秒 ⇒ 一次成功(打包与构建退出码都检查了) |
| 验证规模 | 53 行判据;6 个具名组件全可用;10 个原型方法全命中;BTN_HEIGHT=50 / generateAlphabet⇒[A,B,C] / getIndex("B")⇒1 三条精确值命中;openModal() 状态机全程通过;3 条未通过(AnimationTypeEnum undefined、defaultProps 13≠我写的 14、getFilteredData('ap') 返回 0);LogBox 修正后无通知;4 张截图互异 |
⚠️ 基线说明:交付包
spec.json声明RN 0.88.0 + RNOH 0.84.4(两字段自相矛盾),本次验证在RN 0.84.1 + RNOH 0.84.3上完成。
本库无已移除写法、Platform.OS只有一处惰性分支 ⇒ 结论跨基线成立。
相关社区与项目地址
| 项目 | 地址 |
|---|---|
| React Native 鸿蒙官方社区(CPF-RN) | https://atomgit.com/CPF-RN |
| RNOH 框架仓库 | https://atomgit.com/openharmony-RN/ohos_react_native |
| RNOH 三方库使用文档 | https://atomgit.com/OpenHarmony-RN/usage-docs |
| React Native for OpenHarmony 组织 | https://atomgit.com/oh-react-native |
| RN 三方库鸿蒙适配清单 | https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview |
欢迎加入 CPF-RN 鸿蒙社区:https://atomgit.com/CPF-RN
React Native for OpenHarmony 组织:https://atomgit.com/oh-react-native
RN 三方库鸿蒙适配清单:https://atomgit.com/oh-react-native/rn-ohos-adaptation-overview
更多推荐



所有评论(0)