
本文记录把react-native-searchable-dropdown可搜索下拉选择适配到 HarmonyOS 的完整过程。它做的事很实用一个输入框 一个可搜索的列表。点输入框弹出全部选项输入关键字实时过滤点某一项就选中、列表收起还支持多选 chips 行每个已选项是一个带X的小胶囊。结论这属于最省事的一类库—— 纯 JS、零依赖、没有一处已移除的 API。装完直接用。★★ 一个差点写错的细节render不在原型上第一次扫源码时就发现它的成员写法是混着的exportdefaultclassSearchableDropDownextendsComponent{constructor(props){…}// ← 原型方法renderFlatList(){…}// ← 箭头函数【类字段】componentDidMount(){…}// ← 箭头函数【类字段】searchedItemssearchedText{…}renderItems(item,index){…}renderListType(){…}renderTextInput(){…}render(){…}// ← ★★ 连 render 都是箭头字段renderSelectedItems(){…}// ← 原型方法}实测原型方法命中数constructor / renderSelectedItems 2 ✓ 7 个箭头类字段在 prototype 上的个数 0 ✓ ★★ prototype.render undefined ✓⇒ 如果按render一定在原型上去写断言第一条就会挂✓这类判断容易反复踩所以写断言前先看源码写法已经成了固定动作★★★ 三处可以精确预言的几何/数值实测全部命中①defaultIndex⇒ 输入框初值componentDidMount(){constlistItemsthis.props.items;constdefaultIndexthis.props.defaultIndex;if(defaultIndexlistItems.lengthdefaultIndex){this.setState({listItems,item:listItems[defaultIndex]});// ← 取第 defaultIndex 项}else{this.setState({listItems});}};实测items [Apple, Banana, Cherry, Durian, Elderberry]、defaultIndex{2}TextInput#TextInput(94) [36,1572,1284,1694] Cherry⇒ 初值正好是items[2].name✓而不传defaultIndex的对照实例输入框是空的✓②focus决定列表是否渲染天然 A/BrenderFlatList(){if(this.state.focus){// ← 只有聚焦时才返回 FlatList…returnFlatList …/;}};实测两个方向状态dump 里的列表项未聚焦0 项页面里根本没有列表文字点一下输入框聚焦5 项全在Apple/Banana/Cherry/Durian/Elderberry③ chips 的宽度有公式// renderSelectedItems()multi chip selectedItems.length 0 时才渲染View key{index}style{{width:(item.name.length*8)60,// ★ 宽度 名字长度 × 8 60vp…}}Text{item.name}/TextTouchableOpacity onPress{…onRemoveItem…}TextX/Text/TouchableOpacity/View实测selectedItems [Apple, Durian]density 3chip公式预言宽度实测Apple5 字符5 × 8 60 100vp300pxx51..351 (w300)✓Durian6 字符6 × 8 60 108vp324pxx381..705 (w324)✓⇒两个 chip 的宽度都是公式的精确结果✓✓★★ 交互也通了点列表里的BananaSELECT D1 nameBanana id2⇒onItemSelect(item)收到的条目完全正确 ✓而且选完之后列表自动收起源码里setState({item, focus:false})Keyboard.dismiss()✓设备侧断言A 9 / 9 B 4 / 4 13 / 13 纯断言全通过交互组 2 条为测试脚手架问题详见第六节其中点选已单独取得证据。一、先说结论项结果上游最新版1.1.3上游license 字段为空交付包spec.json记为ISC上游 star148库类型纯 JS 单文件组件库index.js9016 B源码直发是否需要原生适配不需要无harmony/、无NativeModules运行时依赖✅零prepare/postinstall✅ 都没有mainindex.js需要 HAR / 权限 / ohpm❌ 都不需要★ 是否可用✅可以直接用零老 API、零平台分支设备侧断言✅纯断言 13 / 13A 9 B 4交互证据点选回调 ✓、focus门控 ✓★★★defaultIndex实测输入框初值 items[2].name Cherry✓★★★focus门控未聚焦 0 项 / 聚焦 5 项 ✓★★★★ chips 宽度name.length*860⇒Apple 300px / Durian 324px精确命中✓★ 交互onItemSelect(nameBanana, id2)✓选完列表自动收起 ✓★ 结构要点render是箭头类字段⇒prototype.render是undefined⚠️ 未验证向输入框注入文本onTextChange/ 实时过滤未实测二、判定与接入步做法结果①package.json里有没有harmony.autolinking没有②仓库里有没有harmony/没有③代码里有没有NativeModules全无④import 图体检只有reactreact-native⇒零依赖✓⑤scripts扫prepare家族✅ 没有⑥★ 老 API 预扫只命中import React, { Component } from react⇒Component仍存在 ⇒ 安全不是PropTypes⑦★ 平台分支预扫✅ 空⑧原生痕迹预扫✅ 空⑨★ 成员写法预扫混用2 个原型方法 7 个箭头类字段含render⇒ 断言前先分组 ✓npminstallgithttps://atomgit.com/oh-react-native/react-native-searchable-dropdown.git#1.1.3-ohos-1.0.0--ignore-scripts装完即可用。三、主要 props 与行为读源码整理prop作用items数据源形如[{id, name}]defaultIndex初值取items[defaultIndex]要求listItems.length defaultIndexmulti/chip/selectedItems多选 chips 行宽度name.length*860onTextChange/onChangeText输入变化回调onTextChange优先也支持放在textInputProps里用setTimeout(…, 0)调用onItemSelect选中回调同样setTimeout(…, 0)onRemoveItem点 chip 上的X时回调onFocus/onBlur聚焦/失焦聚焦时列表恢复为props.items全量setSort自定义过滤函数默认item.name.toLowerCase().indexOf(text.toLowerCase()) -1大小写不敏感的子串匹配listProps透传给内部FlatList已有的键不被默认值覆盖但style会被强制覆盖resetValue选中后清空并重新聚焦containerStyle/textInputStyle/itemsContainerStyle/itemStyle/itemTextStyle/placeholder/placeholderTextColor/underlineColorAndroid/textInputProps样式与透传四、实测结果4.1 断言 13 / 13纯断言部分组内容结果A 契约与结构默认导出是 class、只有default一键、2 个原型方法 7 个箭头类字段含render、prototype.render undefined、FlatList/Keyboard.dismiss存在、4 条源码级事实9/9B 渲染与几何4 个实例渲染未抛错、标题安全区vp ≥39、三条判据与 chips 宽度预言4/4C 交互与边界输入回调 / 点选回调 / 4 条边界记录1/3⚠️ 见第六节4.2 关键观测值默认导出的 typeof function 原型方法命中数constructor / renderSelectedItems 2 7 个箭头类字段在 prototype 上的个数 0 ★★ prototype.render undefined D1defaultIndex2TextInput [36,1572,1284,1694] Cherry ← items[2].name ✓ D1 未聚焦dump 里 0 个列表项 D1 聚焦后Apple / Banana / Cherry / Durian / Elderberry5 项全在✓ 点 BananaSELECT D1 nameBanana id2 ← onItemSelect ✓ 点选后列表消失focusfalse Keyboard.dismiss✓ D3 chips Apple ⇒ Custom#View [51,117,351,131] w300 5×860100vp300px✓ Durian ⇒ Custom#View [381,117,705,131] w324 6×860108vp324px✓ UI 安全区TITLE measureInWindow y420px ≥117 ✓vp ≥39 ✓五、已知限制与本次验证边界5.1 用这个库要知道的事项说明★render是箭头类字段prototype.render是undefinedfadeIn之类的方法也走同一风格★defaultIndex有效的前提listItems.length defaultIndex否则不生效★ 只有聚焦才渲染列表renderFlatList()里if (this.state.focus)★listProps.style会被强制覆盖其它键则是未提供才补★ 聚焦时列表恢复全量onFocus里listItems: this.props.items★ 回调是setTimeout(…, 0)里调的不要在回调里假设同步发生在点击的同一帧★ 默认匹配器是子串匹配想改成前缀/模糊匹配就传setSort★ chips 宽度按字符数算name.length*860⇒ 中文/emoji 名字会偏窄按字符数而非显示宽度⚠️onBlur里item被设成selectedItems数组下一帧value item.name变成undefined⇒ 输入框可能变成非受控本轮未实测⚠️ 上游 license 字段为空npm 与仓库都没有 license 字段交付包spec.json记为 ISC5.2 本次验证的边界★ 向输入框注入文本未成功uitest uiInput inputText没有写进 RNOH 的TextInput⇒onTextChange回调与实时过滤未实测见第六节onBlur后value变undefined的疑点未实测resetValue未验证setSort自定义匹配器未验证只用了默认的大小写不敏感只做了源码级确认onRemoveItem点 chip 的 X未实测listProps.style被强制覆盖只做了源码级说明没有对比两次渲染textInputProps的各种透传组合未穷举Keyboard.dismiss()的效果未做断言只观察到列表收起多选题的renderItems分支命中selectedItems时显示 X未逐项核对长列表滚动/性能未测只在一台模拟器上验证Pura X Viewdensity 31320×2232没有真机未做跨平台对照。未改动库代码。六、★ 本轮没做成的一件事如实记录我原本要把输入关键字 → 实时过滤也验掉用uitest uiInput往输入框里写字但两次尝试都没写进去点输入框列表正常弹出 ⇒ 说明 onFocus 生效了 ✓ uitest uiInput inputText an ⇒ 输入框文本没变、列表没过滤、onTextChange 没触发 ✗ 再试逐字符输入 a / n ⇒ 同样没进去 ✗⇒ 现象是聚焦成功、但文本注入无效。我没有继续深挖可能是注入通道与 RNOH 的TextInput未打通也可能是需要软键盘先起来。⇒所以这一条我不写成已验证onTextChange与实时过滤在本轮是未实测✓⇒ 而同一条链路的另一端点选是实测成功的SELECT D1 nameBanana id2✓focus门控也两个方向都实测了 ✓★ 顺带说明输入文本这类交互在模拟器上不一定可靠—— 判断功能是否正常时最好挑一条不依赖输入法的链路本轮的defaultIndex、focus门控、点选、chips 几何都属于这类✓七、我自己的两个失误#失误现象纠正1又在 JSX 文本位置写了花括号我写TextD4 给 listProps{{nestedScrollEnabled:true, style:{...}}}…/Text⇒style:{...}是非法 JS空 spread⇒ Babel 报Unexpected token⇒打包失败改成不含花括号的纯文本描述 ✓2FlatList的typeof猜成objectRN 0.84 里是function/组件⇒ 挂 1 条改成断言存在 ✓失误 1 已经是同一类问题的第二次JSX 的文本位置不能直接出现{…}—— 会被当成表达式容器{{…}}甚至 spread child导致语法/编译错误。想显示这种字面量必须包成字符串表达式{{…}}。八、常见问题Q1它在 RN 0.84 上能跑吗能。纯 JS、零依赖、没有一处已移除的 API唯一从react取的具名导入是Component仍然存在。本工程实测打包与构建都成功、页面正常渲染、defaultIndex/focus门控 / 点选 / chips 几何全部符合预期 ✓Q2为什么我拿到的实例上prototype.render是 undefined因为它的render是箭头函数类字段render () {…}挂在实例上而不是原型上。同一风格还有renderFlatList/componentDidMount/searchedItems/renderItems/renderListType/renderTextInput只有constructor与renderSelectedItems是原型方法。Q3列表什么时候出现只有state.focus为真时才会渲染内部FlatList—— 所以要点一下输入框聚焦时列表会恢复成props.items全量。Q4defaultIndex怎么用SearchableDropDown items{ITEMS} defaultIndex{2} … / // 初值 ITEMS[2].name⚠️ 它有个前提listItems.length defaultIndex。Q5chip多选小胶囊的宽度能改吗源码里是写死的公式width item.name.length * 8 60vp。想改宽度要自己覆盖样式或改源码 —— 注意它是按字符个数算的中文/emoji 名字会偏窄。Q6listProps里的样式为什么不生效因为库里有这几行if(!Object.keys(flatListPorps).includes(kv.key)){flatListPorps[kv.key]kv.val;}else{if(kv.keystyle){flatListPorps[style]kv.val;}}// ← style 被强制覆盖⇒style总是用itemsContainerStyle其它键如nestedScrollEnabled你传了就不覆盖。Q7输入变化的回调是同步的吗不是 —— 源码用setTimeout(…, 0)调onTextChange/onItemSelect。别在回调里假设和点击同一帧。Q8这个库需要鸿蒙侧做什么特殊处理吗不需要。纯 JS、零依赖、无原生代码、无平台分支 —— 直接装、直接用 ✓小结react-native-searchable-dropdown是纯 JS 的可搜索下拉单文件 9016 B、零依赖。设备侧纯断言 13 / 13 通过并且有三处可精确预言的数值/几何全部命中。最值得记下来的是这几条★★★defaultIndex可以直接预言输入框初值。实测defaultIndex{2}⇒ 输入框读到Cherry正好items[2].name不给defaultIndex的对照实例为空 ✓★★★focus门控是一个天然 A/B。renderFlatList()里if (this.state.focus)⇒未聚焦 0 项 / 聚焦 5 项两个方向都实测 ✓⇒ 这类开关型行为最好验证不用管动画、不用管时序只要看 dump 里有没有东西。★★★★ chips 的宽度有闭式公式实测精确命中。width name.length * 8 60vp⇒Apple(5) 预报 300px、Durian(6) 预报 324px ⇒ 实测w300与w324✓✓⇒ 有公式的几何先算再量一比就知道对不对。★★ 结构断言前必须先看写法。这个库连render都是箭头类字段⇒prototype.render undefined。预扫阶段把它分成2 个原型方法 7 个箭头字段之后断言一次就对 ✓★ 交互验证要挑不依赖输入法的链路。本轮的点选onItemSelect、聚焦门控、初值都拿到了实测证据而向输入框注入文本两次都没成功 ⇒ 这一条如实标为未验证而不是推测它能过滤 ✓★ 我又在 JSX 文本里写了{…}这已是第二次⇒ 打包直接失败。⇒JSX 文本位置的花括号必须包成字符串表达式。本篇用到的库项内容三方库react-native-searchable-dropdown上游1.1.3的鸿蒙适配版适配仓库https://atomgit.com/oh-react-native/react-native-searchable-dropdown适配 TAG1.1.3-ohos-1.0.0需要 HAR / 权限 / ohpm都不需要纯 JS、零依赖库类型纯 JS 组件库可搜索下拉选择单选 / 多选 chips运行时依赖✅零prepare/postinstall✅ 都没有许可证交付包spec.json记为ISC⚠️ 上游 npm 与仓库的 license 字段为空上游仓库https://github.com/zubairpaizer/react-native-searchable-dropdown★148宿主工程RNOH084Demo测试页rnAppKey SearchableDropdownTestApp# 从适配仓库安装零依赖npminstallgithttps://atomgit.com/oh-react-native/react-native-searchable-dropdown.git#1.1.3-ohos-1.0.0--ignore-scripts# 本地目录接入npminstall../react-native-searchable-dropdown --ignore-scriptsimport SearchableDropDown from react-native-searchable-dropdown; // ① 单选 初值 SearchableDropDown items{[{id: 1, name: Apple}, {id: 2, name: Banana}, {id: 3, name: Cherry}]} defaultIndex{2} // 初值 items[2].name ⇒ 实测输入框显示 Cherry placeholder搜索 onTextChange{(text) console.log(输入, text)} // 用 setTimeout(…, 0) 回调 onItemSelect{(item) console.log(选中, item)} // 实测收到 {id:2,name:Banana} onFocus{() {}} onBlur{() {}} containerStyle{{padding: 8}} textInputStyle{{padding: 10, borderWidth: 1, borderColor: #aaa, color: #111}} itemsContainerStyle{{maxHeight: 160}} itemStyle{{padding: 10, backgroundColor: #fff}} itemTextStyle{{color: #111}} / // ② 多选 chipschip 宽度 name.length*860库内写死 SearchableDropDown items{ITEMS} multi chip selectedItems{[{id: 1, name: Apple}, {id: 4, name: Durian}]} onRemoveItem{(item) console.log(移除, item)} … / // ③ 自定义过滤默认是大小写不敏感的子串匹配 SearchableDropDown setSort{(item, text) item.name.startsWith(text)} … / // ④ 透传 listProps注意style 会被 itemsContainerStyle 强制覆盖 SearchableDropDown listProps{{nestedScrollEnabled: true}} … / // ⚠️ render / renderFlatList / componentDidMount 等都是【箭头类字段】不在 prototype 上 // ⚠️ 只有 state.focus 为真时才渲染内部 FlatList聚焦时列表恢复为 items 全量 // ⚠️ onBlur 里 item 会被设成 selectedItems数组⇒ value 变 undefined本轮未实测# 换页启动测试页 —— force-stop 不能省hdc shell aa force-stop com.rnoh084.demo hdc shell aa start-bcom.rnoh084.demo-aEntryAbility--psrnAppKey SearchableDropdownTestApp判据的复现方式# ① 初值直接读输入框文本dump 里的 TextInput 节点带 valuedevecocli ui layout|Select-StringTextInput#TextInput# ⇒ Cherry# ② focus 门控对比未聚焦/聚焦两次 dump 里的列表项个数devecocli ui layout|Select-StringApple|Banana|Cherry# 未聚焦 ⇒ 0聚焦 ⇒ 全部# ③ chips 宽度width name.length*8 60vp⇒ ×density 得像素再与 dump 里的 Custom#View 宽度比devecocli ui layout|Select-StringCustom#View# ⇒ Apple w300 / Durian w324# ④ 点选点列表项读回调日志hdc shelluitest uiInput click x yhdc shellhilog -x | grep sdropdown-test# ⇒ SELECT D1 nameBanana id2# ⚠️ 文本注入uitest uiInput inputText本轮未成功 ⇒ 想验实时过滤需另找注入方式验证环境项版本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本次构建首次因JSX 花括号语法错误打包失败未装机⇒assembleHap3 分 55 秒13/16⇒ 4 分 14 秒 ⇒A 9/9 B 4/4验证规模设备侧纯断言 13 / 13初值 1 项Cherryfocus 门控 2 方向0 项 / 5 项chips 宽度 2 项300px / 324px 精确命中点选回调 1 次nameBanana id24 张截图互异相关社区与项目地址项目地址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