本文记录把 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 本次验证的边界

  1. ★★★ 未验证"点选择框弹出列表"的真实触摸路径(用 openModal() 编程调用);
  2. ★★★ 未验证列表项的点击选中(onSelectMethod / onSelected 回调未触发);
  3. ★★★ Modal 打开后列表项文本未在布局 dump 里定位到(只确认了选择框文本 PMV 点我选择 y=1163);
  4. ★★ AnimationTypeEnum 仍是 undefined(子路径导入失败,原因未查明);
  5. ★★ getFilteredData('ap') 返回 0 项(与源码逻辑不符,未查明);
  6. ★★ 未验证字母索引栏(AlphabetComponent)的点击跳转;
  7. ★★ 未验证滚动到底部的"回到顶部"按钮(ScrollToTopComponent);
  8. 未验证 renderSelectView / renderListItem / renderSearch 三个自定义渲染入口;
  9. 未在 RN 0.88.0 / RNOH 0.84.4 上复验;
  10. 未测其它屏幕宽度;未做跨平台对照。

库代码未改动。


十、常见问题

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、老写法九项全空 —— 代码本身很干净。

最值得记下来的是这几条:

  1. ★★★★★ “源码的导出"不等于"运行时的导出”。
    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/)。

  2. ★★★★★ 精确值判据三条全部一次命中。
    CommonStyle.BTN_HEIGHT = 50、generateAlphabet(4项) ⇒ ["A","B","C"]、getIndex("B") ⇒ 1
    —— 都是先在设备外用源码逻辑推出真值、再到设备上断言同一个值 ✓

  3. ★★★★★ 成员归属再添一例(这次是"原型方法")。
    10 个公开方法都是 public x(): void { this._x(); } ⇒ 全在 prototype 上、断言一次通过 ✓
    ⇒ 与某些库里那 6 个箭头类字段(只在实例上)正好构成对照 ⇒
    判别依据始终是 .d.ts 的写法:name(…): … vs name: (…) => … ✓

  4. ★★★★ openModal() 的状态机可观测且全程通过:
    初始 modalVisible = false → 调用 → true;setText('ap') → state.searchText = 'ap' ✓
    ⇒ 这类"状态位移"是验证实例方法最直接的证据。

  5. ★★★ 我又在"数量类断言"上错了(连续第四轮)。
    defaultProps 实际 13 项、我写了 14 ✗ —— 而且我已经把真值 13 打成 NOTE 了,仍写错字面量。
    ⇒ 改法必须从"数量"改成"清单":把 .d.ts 里的键名逐个列成数组比对,
    数量错、名字错都会被抓到,比断言一个数字可靠得多 ✓

  6. ★★★ 老实承认两条"未解释"的偏差:
    AnimationTypeEnum 仍 undefined、getFilteredData('ap') 返回 0 项(源码逻辑上应匹配 2 项)。
    ⇒ 没有查明根因,就不写成"库有 bug" —— 只写"实测与预期不符、原因未查明" ✓

  7. ★★ NativeModules 命中要看上下文:2 处全在注释里(// const STATUSBAR_HEIGHT = …)⇒ 不是真实使用 ✓


本篇用到的库

项内容
三方库react-native-picker-modal-view(上游 1.3.2 的鸿蒙适配版)
适配仓库https://atomgit.com/oh-react-native/react-native-picker-modal-view
适配 TAG1.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 Native0.84.1
React19.2.3
RNOH(npm / ohpm)@react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3
Node.js / npmv24.14.0 / 11.9.0
DevEco Studio26.0.0.621
HarmonyOS SDKAPI 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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐