本文记录把 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 / defaultposition:'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
项数值
首次 assembleHap6 分 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 读回渲染出的列表项
Chiquery=Chi picked= 结果数=2China(y 1786…1883)、Chile(y 1883…1980)
Caquery=Ca picked= 结果数=2Canada(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(有列表)1283China @ y 1253…1290
hideResults=true(隐藏)1283—
hideResults=false(恢复)1283China @ y 1253…1290

两条结论:

  1. MARKER 从不移动 ⇒ 下拉不推挤下方内容;
  2. 列表项的 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=false2✅ Canada / Cambodia
hideResults=true2❌ 一个都没有

⇒ 即使 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 库本身的

  1. 默认下拉是 FlatList(虚拟化列表),放进 ScrollView 会触发 VirtualizedLists should never be nested...(E 级日志 + 红色提示)。 需用 renderResultList 换非虚拟化列表,或不把组件放进 ScrollView。
  2. onShowResults 在 render 期间被调用,回调里不能 setState。
  3. Platform.select 只给了 android/ios/default,鸿蒙落在 default(iOS 式绝对定位浮层)。这意味着鸿蒙上的下拉是浮层覆盖,不会推挤下方布局——如果某个页面依赖"下拉把内容推下去"的旧行为,会不一致。
  4. hideResults=true 时不渲染列表容器,即使 data 非空。
  5. dist/index.js 含 JSX 且是 ESM,main 直接指向它,且没有 "type": "module" ⇒ 纯 Node 无法加载(SyntaxError);只能在 Metro/Babel 环境里消费。(上游同样如此,不是交付方引入的。)
  6. 默认 renderItem 只渲染 <Text>,不可点。要做"点选"必须自己通过 flatListProps.renderItem 或 renderResultList 提供可点元素。
  7. 默认 keyExtractor 是 key-<index>:数据顺序变化时 key 会跟着变,长列表下可能引起不必要的重挂载。

10.2 本次验证的边界

  1. 只在一台模拟器上验证(Pura X View,1320×2232,density 3),没有真机。
  2. 没有验证软键盘:全程用 uitest 注入文本,没有真正调起输入法,所以键盘遮挡/避让行为未测。
  3. onStartShouldSetResponderCapture 未验证(这是"点击下拉时不让输入框失焦"的关键 prop)。
  4. 长列表性能未测:数据集只有 10 条,没有验证几百条时的滚动与 windowing 行为。
  5. renderTextInput 只验证了"被调用过",没有验证自定义渲染出的节点。
  6. flatListProps 只验证了 keyboardShouldPersistTaps 与 renderItem,其余字段未逐一验证。
  7. 只注入了 ASCII 文本,中文/emoji 输入未测。
  8. 没有验证"不放进 ScrollView 就不会报 VirtualizedList 告警"——只验证了"换渲染器能消除告警"。
  9. 没有跨平台对照:没在 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.

两条绕过办法:

  1. 用 renderResultList 传一个纯 View + map 的非虚拟化列表(已实测:告警从 1 条变 0 条);
  2. 不要把组件放进 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 是个只有一个组件、但交互完整的库:打字过滤、下拉建议、点选回填。

这轮的验证方式值得留下的是:

  1. 交互型组件必须做端到端注入 + 回读。 “渲染出来了"不等于"能用”。用 uitest uiInput inputText 注入真实文本、用 devecocli ui layout 读回真实列表、再点击真实浮层项,才把"打字 → 过滤 → 选中"整条链路验实。注入和读取都在应用之外,所以结论不依赖被测代码的自述。

  2. 判断"浮层还是推挤",要用同帧重叠而不是前后位移。 输入框聚焦会让 ScrollView 自动滚动,节点 Y 会整体位移,前后对比不可靠。同一帧里列表项与下方元素的 Y 区间重叠 ⇒ 一定是浮层——这条判据不受滚动影响。

  3. Platform.select 的分支要在设备上确认落到哪一个。 库只给了 android/ios/default,鸿蒙取 default ⇒ iOS 式绝对定位浮层。并且我实测了它的触摸命中也是对的——zIndex 同时作用于绘制与命中,这一点不测是有理由担心的。

  4. "库默认用虚拟化列表"是一条真实的使用约束。 放进 ScrollView 会报 E 级错误。要给出绕过办法并用对照实验证明(默认 1 条告警 → 非虚拟化 0 条),而不是只说"报了个警告"。

  5. 环境类失败要归因到具体依赖。 npm install 退出码 1 出现在本轮,但 error path 指向的是之前接入的那个库。先看 path、再扫依赖的 scripts,否则会把历史缺陷记到当轮头上。

  6. 文件"看着像构建产物"时,先确认上游是不是也这样。 dist/index.js 含 JSX、是 ESM 而 main 直接指它——看着像交付方没构建,实际npm 上游就是同一份文件。先比对再定性,否则会误伤交付方。


本篇用到的库

项内容
三方库react-native-autocomplete-input(上游 5.5.6 的鸿蒙适配版)
适配仓库https://atomgit.com/oh-react-native/react-native-autocomplete-input
适配 TAG5.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 Native0.84.1
React19.2.3
RNOH(npm / ohpm)@react-native-oh/react-native-harmony / @rnoh/react-native-openharmony 0.84.3
Node.jsv24.14.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
宿主 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
**

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐