React Native for OpenHarmony 实战:三方库 react-native-autocomplete-input 的鸿蒙化适配指南
本文记录把 react-native-autocomplete-input(带下拉建议的输入框)适配到 HarmonyOS 的完整过程。
这个库的 API 只有一个组件,但它的价值全在交互上:打字 → 过滤 → 从下拉里挑一条。所以这一轮的验证不能停在"渲染出来了",必须真的注入文本、读回列表、点中下拉项。

一、先说结论
| 项 | 结果 |
|---|---|
| 上游最新版 | 5.5.6(npm gitHead = daa49af9…,无 dependencies) |
| 是否需要原生适配 | 不需要——纯 JS,无原生模块 |
| 与 npm 上游的差异 | 4 / 5 个文件去掉 CR 后逐字符一致,唯一差异是 package.json |
| 自动链接信号 | linked 10 libraries, skipped 1 libraries,计数未增加 |
| 编译 | assembleHap 6 分 23 秒;HAP 81,614,985 → 81,647,755 字节(+32,770 ≈ 32 KB) |
| 设备侧断言 | ✅ 13 / 13 全部通过 |
| 平台样式分支 | 鸿蒙落在 Platform.select 的 default 分支 = iOS 式样式 ⇒ 下拉是绝对定位浮层 |
| 端到端交互 | ✅ 应用外注入文本 → 过滤 → 浮层下拉 → 点击浮层项选中,全链路打通 |
| 浮层/推挤 | ✅ 浮层:MARKER 的 Y 在四种状态下恒为 1283;列表项渲染在 1253…1290,与之重叠 |
| 发现的问题 | ⚠️ 库默认用 FlatList 渲染下拉,放进 ScrollView 会报 E 级错误(已实测绕过办法) |
一句话结论:功能完整可用,而且 iOS 式的绝对定位浮层在鸿蒙上渲染层级与触摸命中都是对的。但有两条使用约束必须知道:默认下拉是虚拟化列表,放进 ScrollView 会报错;以及这个包只能在 RN 打包器里用,纯 Node 加载不了。
二、判定过程:这个库不需要原生适配
判断一个 RN 库要不要做原生适配,我固定走三步:
| 步 | 做法 | 这个库的结果 |
|---|---|---|
| ① | npm view <包名> harmony --json 看有没有 harmony.autolinking | 没有 |
| ② | 仓库里有没有 harmony/ | 没有 |
| ③ | 代码里有没有 NativeModules / Platform.OS / requireNativeComponent | 有 Platform,但只用于样式选择 |
第 ③ 步那处 Platform 恰好是这个库在鸿蒙上唯一需要关注的平台行为:
const styles = StyleSheet.create({
...Platform.select({
android: androidStyles,
ios: iosStyles,
default: iosStyles, // ← 鸿蒙会落到这里
}),
});
Platform.select 只提供了 android / ios / default 三个键。鸿蒙不是 android 也不是 ios,所以取 default = iosStyles。
这直接决定了下拉的呈现方式:
| 分支 | 列表容器样式 | 效果 |
|---|---|---|
android | 无 position(普通流式);外层容器 flex:1 | 下拉会把下方内容推下去 |
ios / default | position:'absolute'、left:0、right:0、borderTopWidth:0;外层容器 zIndex:1 | 下拉浮层覆盖下方内容 |
"鸿蒙上到底是浮层还是推挤"不能靠读代码下结论,必须测。 这是本篇最重要的一项验证,见第六节。
公开 API:只有一个组件,但有 12 个属性。
| 属性 | 说明 |
|---|---|
data | 必填,建议项数组 |
hideResults | 为 true 时整个列表容器不渲染(即使 data 非空) |
onShowResults | 列表显示状态变化回调(注意:在 render 期间被调用) |
renderResultList | 自定义下拉列表渲染器,默认是 FlatList |
renderTextInput | 自定义输入框渲染器,默认是 TextInput |
flatListProps | 透传给下拉列表(含 renderItem / keyExtractor / style 等) |
containerStyle / inputContainerStyle / listContainerStyle | 三层容器样式 |
onStartShouldSetResponderCapture | 用于让点击下拉时不让输入框失焦 |
| 其余 | 透传给内部 TextInput(即所有 TextInputProps) |
默认行为:keyExtractor 是 key-<index>;renderItem 把每项 String() 后放进 <Text>;ref 会转发到内部 TextInput。
三、这个交付包长什么样:与 npm 上游逐字节一致
与 react-native-autocomplete-input@5.5.6 的 npm tarball 逐文件比对(先去 CR 再逐字符比较):
4 / 5 个文件完全一致(dist/index.js、dist/index.d.ts、LICENSE、README.md),唯一不同的是 package.json:
| 字段 | 变化 |
|---|---|
description | 追加 " for React Native for OpenHarmony" |
scripts.test | 由上游的 jest 换成 node --test __tests__/harmony-contract.test.cjs |
repository.url / homepage | 改为 AtomGit 地址 |
files | 删除了上游的白名单(上游为 ["LICENSE","README.md","dist/index.js","dist/index.d.ts"]) |
实现文件零改动,所以后面所有结论都只涉及使用约束,与适配质量无关。
3.1 一个容易误判的点:dist/index.js 不是转译产物
dist/index.js 里仍然写着 JSX:
function defaultRenderItems({ item }) {
return <Text>{String(item)}</Text>;
}
而且它是 ESM 语法(import / export),但 package.json 没有 "type": "module",main 又直接指向它。于是我试着用纯 Node 加载:
$ node -e "require('.../react-native-autocomplete-input')"
require 失败: SyntaxError - Unexpected token '<'
这看起来像"交付方没构建就发布了",但先别急着定性——我去比对了 npm 上游包:上游的 dist/index.js 就是同一份含 JSX 的文件,逐字符一致。所以交付方是如实透传。
不过这条约束是真实的,值得每个使用者知道:
- ✅ Metro / Babel 能编译它,RN 应用完全正常;
- ❌ 纯 Node 加载不了:SSR、Node 脚本、部分测试运行器,以及不做 JSX 处理的打包器都会失败;
- 也就是说,这个包只能在 RN 打包器里消费。交付包的 README 只写了"纯 JavaScript 实现",如果补一句这个约束会更有用。
四、接入宿主与构建运行
纯 JS 库的接法只有三处:
// package.json
"react-native-autocomplete-input": "file:../react-native-autocomplete-input"
// metro.config.js —— file: 装进来是 junction,必须让 Metro 找得到源码
watchFolders: [path.resolve(__dirname, '../react-native-autocomplete-input')],
// index.js
AppRegistry.registerComponent('AutocompleteTestApp', () => AutocompleteTestApp);
不要动 harmony/entry/oh-package.json5(不需要 HAR),不要动 module.json5(不需要权限)。信号是自动链接计数不增加:
info updated 4 file(s), linked 10 libraries, skipped 1 libraries
| 项 | 数值 |
|---|---|
首次 assembleHap | 6 分 23 秒 |
| 二次重编(改了一条断言 + 加了非虚拟化列表模式) | 6 分 17 秒 |
| HAP 变化 | 81,614,985 → 81,647,755 字节(+32,770 ≈ 32 KB) |
| 其中库本身 | dist/index.js 仅 2,843 字节 |
⚠️ 本轮宿主的
npm install依然是退出码 1,但不是这个库造成的。见第九节。
五、验证设计:交互型组件怎么验
这个库没有返回值、没有系统状态,唯一的验收标准是"用它真的能完成一次选择"。所以验证设计围绕三件事。
5.1 用应用外工具注入真实文本、读回真实结果
设备自带 uitest,可以直接注入输入与点击:
hdc shell "uitest uiInput inputText <x> <y> <文本>" # 在坐标处输入
hdc shell "uitest uiInput click <x> <y>" # 点击坐标
配合布局读取:
devecocli ui layout # 输出每个文本节点的内容与矩形
于是整条验证链都在被测应用之外:注入不经过库,读取也不经过库。
5.2 页面里放一个 MARKER 来判断"浮层还是推挤"
测试页在输入框正下方放了一个 MARKER-下方内容:
- 下拉出现后 MARKER 的 Y 不变 ⇒ 浮层;
- MARKER 被推下去 ⇒ 流式。
5.3 一个测量陷阱:聚焦输入框会让页面自动滚动
输入框获得焦点时,ScrollView 会自动滚动把它保持可见,所有节点的 Y 会整体位移。所以"下拉出现前后对比 Y"这种做法在跨焦点状态时不可靠。
更稳的判据是"同一帧内的重叠":
如果列表项的 Y 区间与 MARKER 的 Y 区间重叠,那它一定是浮层——流式布局下两者不可能重叠。
这条不受滚动影响,是本篇采用的主判据。
5.4 分组口径
| 组 | 进通过率 | 内容 |
|---|---|---|
| A 契约与导出 | ✅ | 导出形态、Platform.select 分支 |
| B 渲染 / ref / 自定义渲染 | ✅ | ref 转发、自定义 renderResultList、当前状态 |
| C props 行为 | ✅ | onShowResults 调用记录、hideResults 语义、默认 keyExtractor/renderItem |
| 外部协议 | 信息 | 注入文本、读列表、点击浮层项、告警对照 |
六、实测发现一:端到端打通,且下拉是浮层
设备:Pura X View 模拟器,1320×2232(density 3)。
6.1 过滤链路
数据源是 10 个国家名,过滤规则是 c.toLowerCase().includes(query.toLowerCase())。
| 应用外注入 | 布局 dump 读回 | 渲染出的列表项 |
|---|---|---|
Chi | query=Chi picked= 结果数=2 | China(y 1786…1883)、Chile(y 1883…1980) |
Ca | query=Ca picked= 结果数=2 | Canada(y 1786…1883)、Cambodia(y 1883…1980) |
输入框节点自身也回显了内容:
TextInput#TextInput(112) [45,1663,1275,1783] "Chi" clickable longClickable scrollable
⇒ onChangeText → 过滤 → data → 列表渲染 整条链路在鸿蒙上正确。
6.2 浮层判定(滚动无关的判据)
| 状态 | MARKER 的 Y | 列表项 |
|---|---|---|
| 清空后(无列表) | 1283 | — |
输入 Chi(有列表) | 1283 | China @ y 1253…1290 |
hideResults=true(隐藏) | 1283 | — |
hideResults=false(恢复) | 1283 | China @ y 1253…1290 |
两条结论:
- MARKER 从不移动 ⇒ 下拉不推挤下方内容;
- 列表项的 Y 区间(1253…1290)与 MARKER(1283 起)重叠,且起点在 MARKER 之上 ⇒ 它就是浮层。
顺带确认了 left:0 / right:0 也生效:列表项的 X 范围是 45…1275,与输入框的 45…1275 完全对齐。
⇒ 鸿蒙上确实走的是 Platform.select 的 default 分支(iOS 式绝对定位),而且 position:'absolute' + left/right:0 + 容器 zIndex:1 在 RNOH 上都按预期工作。

6.3 点击浮层项(最关键的一条)
浮层下拉正好压在 MARKER(y 1816…1862)上。点击列表项中心 (660, 1834):
点击前: query=Chi picked= 结果数=2
点击后: query=China picked=China 结果数=0
TextInput "China"
已选中:[China]
⇒ 浮层的层级与触摸命中都是对的:点击落在了列表项上(而不是它下方的 MARKER),选中回调触发、输入框被回填、列表自动关闭。
这是本轮最有价值的一条结论:绝对定位浮层不只是"画在上面",它同时也能正确地接收触摸——渲染层级与命中测试是一致的。这一点如果不实测,是有理由担心的(浮层被下层拦截、或 zIndex 只影响绘制不影响命中,都是常见的坑)。

6.4 其余 props 的外部证据
renderResultList 自定义生效——把列表换成自定义渲染器(每项加 ★ 前缀),布局 dump 立刻变成:
★Canada (y 1786..1883)
★Cambodia (y 1883..1980)
★ 只存在于自定义渲染器里,所以这是外部可证的。
hideResults 语义确认:
| 状态 | 结果数 | 列表项是否渲染 |
|---|---|---|
hideResults=false | 2 | ✅ Canada / Cambodia |
hideResults=true | 2 | ❌ 一个都没有 |
⇒ 即使 data 非空(结果数=2),整个列表容器也不渲染。
ref 转发:ref.current 上有 focus / blur / clear / isFocused / setNativeProps,并且实际调用 clear() 成功。
onShowResults:被正常调用,但要注意它在 render 期间被调用——所以回调里不能 setState(会触发 React 的"渲染期间更新其它组件"警告)。需要状态的话,请用 ref 记录、在下一次渲染时读取。
七、实测发现二:默认 FlatList 放进 ScrollView 会报错
renderResultList 的默认值就是 FlatList:
renderResultList: ResultList = FlatList,
而把组件放进 ScrollView(表单页最常见的写法)就会触发 React Native 的错误日志:
E A0beef/#RNOH_JS: VirtualizedLists should never be nested inside plain ScrollViews
with the same orientation because it can break windowing and other functionality
- use another VirtualizedList-backed container instead.
在鸿蒙上它会以底部红色 toast 的形式弹出来(截图里能看到),日志级别是 E(错误)。
7.1 对照实验
为了确认因果,我做了一组对照(清日志 → 注入文本 → 数告警条数):
| 列表渲染方式 | VirtualizedLists should never be nested 告警条数 |
|---|---|
默认(FlatList,在 ScrollView 内) | 1 |
非虚拟化(renderResultList 返回纯 View + map) | 0 |
⇒ 告警确实来自"虚拟化列表嵌套在 ScrollView 内",换成非虚拟化渲染即消失。
7.2 影响与绕过
影响评估:
- 这是库的使用约束,不是鸿蒙特有的——上游在 iOS/Android 上同样会报;
- 功能不会失效:实测下拉仍能正常显示、正常点击;
- 但
VirtualizedList的 windowing 行为在嵌套场景下不可靠(长列表可能出现空白、滚动异常),所以不该忽略。
绕过办法(已实测有效):用 renderResultList 换成非虚拟化列表:
const PlainList = (p: any) => (
<View style={p.style}>
{p.data.map((item: any) => (
<Pressable key={String(item)} onPress={() => onPick(item)}>
<Text>{String(item)}</Text>
</Pressable>
))}
</View>
);
<AutocompleteInput
data={results}
renderResultList={PlainList}
flatListProps={{keyboardShouldPersistTaps: 'handled'}}
...
/>
另一条路是不要把它放进 ScrollView:用 FlatList 当页面根容器,把 autocomplete 作为列表的 header 或一行来用。
八、交付包质量
做得好的:
dist/index.js与dist/index.d.ts与 npm 上游逐字符一致,实现零改动;spec.json的upstream.url是git+ssh://git@github.com/byteburgers/react-native-autocomplete-input.git,与 npm 元数据里的repository.url逐字符相同(这个上游就是用 SSH 形式记录的,交付方照抄、没有自造);native: false与实测一致;scripts.test换成了可运行的node --test(上游是jest,在本仓库跑不起来);- README 对验证范围的说法诚实(“已完成包级 smoke test……”)。
问题:
① README 的「能力一览」太薄,没写组件属性。 只有两行泛泛表述(“组件或工具 API 支持”),而这个库的全部 API 就是那组 props。照这份 README 接入,使用者只能去翻上游文档。
② spec.json 可追溯性偏弱:upstream 是对象而非字符串;没有 upstreamCommit(上游 gitHead = daa49af9… 我从 npm 查到了,但规格文件里没记);validation 是裸字符串 "pass"。三者都导致可追溯性只能靠读 README,无法脚本化核对。
③ 契约测试不测库:
assert.equal(pkg.name, 'react-native-autocomplete-input');
assert.equal(pkg.version, '5.5.6');
assert.ok(require.resolve(path.resolve(__dirname, '..', pkg.main || 'index.js')));
assert.ok(fs.existsSync(path.resolve(__dirname, '..', 'README.OpenHarmony_CN.md')));
只断言包名、版本、主入口文件存在、中文 README 存在。没有渲染组件、没有注入文本、没有验证过滤。命名是诚实的(README 自述为"包级 smoke test"),属于覆盖面不足——而这个库恰好是最好测的一类:一个组件、一份数组、一条过滤规则,十几行测试就能覆盖核心行为。
④ package.json 里上游的 files 白名单被删掉了,发布产物不再受限。
九、一个环境问题的正确归因
本轮宿主的 npm install 依然返回退出码 1。它看起来像是新加的库有问题,但去查 npm 调试日志:
error code 1
error path E:\rnoh-work\<此前接入的某个库>
error command failed
error 'bob' is not recognized as an internal or external command
再扫一遍宿主全部依赖的 scripts:
<此前接入的某个库> -> prepare=bob build
它是唯一带风险生命周期脚本的库,报错的 path 也明确指向它。
⇒ 这不是本轮这个库的问题,而是之前接入的某个库保留了一个消费者侧跑不通的 prepare 脚本(bob build,而 react-native-builder-bob 只是它的 devDependency)。
⇒ 更值得记住的是:这个缺陷会持续影响后续每一轮——只要那个库还在 file: 依赖里,每次 npm install 都会失败,CI 门禁会一直是红的。
通用教训:出现安装类失败时,先看 error path 指向谁、再扫一遍依赖的 scripts。否则很容易把历史问题记到当轮头上,或者反过来漏掉真正的元凶。
十、已知限制
10.1 库本身的
- 默认下拉是
FlatList(虚拟化列表),放进ScrollView会触发VirtualizedLists should never be nested...(E 级日志 + 红色提示)。 需用renderResultList换非虚拟化列表,或不把组件放进ScrollView。 onShowResults在 render 期间被调用,回调里不能setState。Platform.select只给了android/ios/default,鸿蒙落在default(iOS 式绝对定位浮层)。这意味着鸿蒙上的下拉是浮层覆盖,不会推挤下方布局——如果某个页面依赖"下拉把内容推下去"的旧行为,会不一致。hideResults=true时不渲染列表容器,即使data非空。dist/index.js含 JSX 且是 ESM,main直接指向它,且没有"type": "module"⇒ 纯 Node 无法加载(SyntaxError);只能在 Metro/Babel 环境里消费。(上游同样如此,不是交付方引入的。)- 默认
renderItem只渲染<Text>,不可点。要做"点选"必须自己通过flatListProps.renderItem或renderResultList提供可点元素。 - 默认
keyExtractor是key-<index>:数据顺序变化时 key 会跟着变,长列表下可能引起不必要的重挂载。
10.2 本次验证的边界
- 只在一台模拟器上验证(
Pura X View,1320×2232,density 3),没有真机。 - 没有验证软键盘:全程用
uitest注入文本,没有真正调起输入法,所以键盘遮挡/避让行为未测。 onStartShouldSetResponderCapture未验证(这是"点击下拉时不让输入框失焦"的关键 prop)。- 长列表性能未测:数据集只有 10 条,没有验证几百条时的滚动与 windowing 行为。
renderTextInput只验证了"被调用过",没有验证自定义渲染出的节点。flatListProps只验证了keyboardShouldPersistTaps与renderItem,其余字段未逐一验证。- 只注入了 ASCII 文本,中文/emoji 输入未测。
- 没有验证"不放进
ScrollView就不会报 VirtualizedList 告警"——只验证了"换渲染器能消除告警"。 - 没有跨平台对照:没在 iOS/Android 上跑同一份用例比对行为。
未改动库代码。
十一、常见问题
Q1:需要 HAR、权限或 ohpm 接线吗?
都不需要。纯 JS 库,接进宿主只改 package.json、metro.config.js、index.js 三处。判断信号是 link-harmony 计数不增加(本次 linked 10 libraries, skipped 1 libraries)。
Q2:怎么用?
import AutocompleteInput from 'react-native-autocomplete-input';
const DATA = ['China', 'Chile', 'Canada', 'Japan'];
const [query, setQuery] = useState('');
const [picked, setPicked] = useState('');
const results = query === picked ? [] : DATA.filter(c => c.toLowerCase().includes(query.toLowerCase()));
<AutocompleteInput
data={results}
value={query}
onChangeText={setQuery}
placeholder="输入国家名"
flatListProps={{
keyboardShouldPersistTaps: 'handled',
renderItem: ({item}) => (
<Pressable onPress={() => { setPicked(item); setQuery(item); }}>
<Text>{item}</Text>
</Pressable>
),
}}
/>
注意:默认的 renderItem 只渲染 <Text>、不可点。要做点选必须自己传 renderItem 或 renderResultList。
Q3:它在鸿蒙上的下拉是浮层还是会把内容推下去?
浮层(覆盖)。因为 Platform.select 只有 android/ios/default 三个键,鸿蒙取 default = iOS 式样式:列表 position:'absolute'、left:0、right:0,外层容器 zIndex:1。
实测证据:输入框正下方的 MARKER 在"有下拉/无下拉"两种状态下 Y 都是 1283 完全不变,而列表项渲染在 1253…1290,与 MARKER 重叠。
Q4:浮层能不能点到?会不会被下层拦截?
能正常点到。 实测点击浮层里的 China:picked=China、已选中:[China]、输入框回填、列表关闭。⇒ zIndex 同时作用在绘制层级和触摸命中上,两者一致。
Q5:为什么把组件放进 ScrollView 会弹红色报错?
因为库默认用 FlatList 渲染下拉,而 FlatList 是虚拟化列表,不允许嵌套在普通 ScrollView 里(同方向会破坏 windowing)。告警原文:
VirtualizedLists should never be nested inside plain ScrollViews with the same orientation
because it can break windowing and other functionality - use another VirtualizedList-backed
container instead.
两条绕过办法:
- 用
renderResultList传一个纯View+map的非虚拟化列表(已实测:告警从 1 条变 0 条); - 不要把组件放进
ScrollView——用FlatList当页面根容器,把它作为 header 或一行。
Q6:hideResults 是干什么的?
为 true 时整个列表容器都不渲染,即使 data 非空。实测:data 有 2 条时,hideResults=true 下单一条也不渲染。
它和"传空数组"的区别是:传空数组时列表容器仍然存在(只是没有项),而 hideResults 是连容器都不渲染。
Q7:为什么我不能在 Node 里 require 这个包?
因为 dist/index.js 含 JSX 语法(return <Text>{...}</Text>),且是 ESM 而 package.json 没有 "type": "module"。纯 Node 会报 SyntaxError: Unexpected token '<'。
这不是交付方的问题——npm 上游就是同一份文件。但这个包只能在 Metro/Babel 环境里消费:RN 应用没问题,SSR 或 Node 脚本用不了。
Q8:ref 能拿到什么?
会转发到内部 TextInput。实测 ref.current 上有 focus / blur / clear / isFocused / setNativeProps,clear() 调用成功。
Q9:onShowResults 里能 setState 吗?
不能。 它在 render 期间被调用,setState 会触发 React 的"渲染期间更新其它组件"警告。需要保存状态的话,用 useRef 记录,在下一次渲染时读出来。
小结
react-native-autocomplete-input 是个只有一个组件、但交互完整的库:打字过滤、下拉建议、点选回填。
这轮的验证方式值得留下的是:
-
交互型组件必须做端到端注入 + 回读。 “渲染出来了"不等于"能用”。用
uitest uiInput inputText注入真实文本、用devecocli ui layout读回真实列表、再点击真实浮层项,才把"打字 → 过滤 → 选中"整条链路验实。注入和读取都在应用之外,所以结论不依赖被测代码的自述。 -
判断"浮层还是推挤",要用同帧重叠而不是前后位移。 输入框聚焦会让
ScrollView自动滚动,节点 Y 会整体位移,前后对比不可靠。同一帧里列表项与下方元素的 Y 区间重叠 ⇒ 一定是浮层——这条判据不受滚动影响。 -
Platform.select的分支要在设备上确认落到哪一个。 库只给了android/ios/default,鸿蒙取default⇒ iOS 式绝对定位浮层。并且我实测了它的触摸命中也是对的——zIndex同时作用于绘制与命中,这一点不测是有理由担心的。 -
"库默认用虚拟化列表"是一条真实的使用约束。 放进
ScrollView会报 E 级错误。要给出绕过办法并用对照实验证明(默认 1 条告警 → 非虚拟化 0 条),而不是只说"报了个警告"。 -
环境类失败要归因到具体依赖。
npm install退出码 1 出现在本轮,但error path指向的是之前接入的那个库。先看path、再扫依赖的scripts,否则会把历史缺陷记到当轮头上。 -
文件"看着像构建产物"时,先确认上游是不是也这样。
dist/index.js含 JSX、是 ESM 而main直接指它——看着像交付方没构建,实际npm 上游就是同一份文件。先比对再定性,否则会误伤交付方。
本篇用到的库
| 项 | 内容 |
|---|---|
| 三方库 | react-native-autocomplete-input(上游 5.5.6 的鸿蒙适配版) |
| 适配仓库 | https://atomgit.com/oh-react-native/react-native-autocomplete-input |
| 适配 TAG | 5.5.6-ohos-1.0.0 |
| 需要 HAR / 权限 / ohpm | 都不需要(纯 JS) |
| 上游仓库 | https://github.com/byteburgers/react-native-autocomplete-input(基线 commit daa49af9b7e311b194892171ab5c98c83b81ab8e,MIT) |
| 宿主工程 | RNOH084Demo(测试页 rnAppKey = AutocompleteTestApp) |
接入方式(纯 JS,零原生接线):
// package.json
"react-native-autocomplete-input": "file:../react-native-autocomplete-input"
// metro.config.js —— file: 装进来是 junction,必须让 Metro 找得到源码
watchFolders: [path.resolve(__dirname, '../react-native-autocomplete-input')],
import AutocompleteInput from 'react-native-autocomplete-input';
// 推荐写法:自己提供可点的 renderItem(默认的不可点),
// 若外层是 ScrollView 则用 renderResultList 换非虚拟化列表
const PlainList = (p: any) => (
<View style={p.style}>
{p.data.map((item: any) => (
<Pressable key={String(item)} onPress={() => onPick(item)}>
<Text>{String(item)}</Text>
</Pressable>
))}
</View>
);
<AutocompleteInput
data={results}
value={query}
onChangeText={setQuery}
placeholder="输入国家名"
hideResults={query === picked}
onShowResults={show => { showLog.current.push(show); }} // ⚠️ render 期间调用,勿 setState
renderResultList={PlainList} // 避开 VirtualizedList 嵌套告警
flatListProps={{keyboardShouldPersistTaps: 'handled',
renderItem: ({item}) => (
<Pressable onPress={() => onPick(item)}><Text>{item}</Text></Pressable>
)}}
containerStyle={{margin: 8}}
/>
# 换页启动测试页(force-stop 不能省,换页参数只在冷启动生效)
hdc shell aa force-stop com.rnoh084.demo
hdc shell aa start -b com.rnoh084.demo -a EntryAbility --ps rnAppKey AutocompleteTestApp
# 应用外注入文本 + 读回渲染结果
hdc shell "uitest uiInput inputText 660 1723 Chi"
devecocli ui layout | Select-String 'query=|China|Chile'
验证环境
| 项 | 版本 |
|---|---|
| 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 | v24.14.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 |
| 宿主 HAP 产物 | entry-default-signed.hap(81.65 MB) |
| 本次增量构建 | assembleHap 6 分 23 秒,HAP +32,770 字节(库本身 2,843 字节) |
| 验证规模 | 设备侧 13 / 13 断言全部通过;端到端交互 2 组过滤 + 1 次浮层点选;浮层判定 4 个状态 MARKER Y 恒为 1283;VirtualizedList 告警对照 1 → 0 |
欢迎加入 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)