news 2026/10/19 2:13:15

React Native for OpenHarmony 实战:三方库 react-native-action-button 的鸿蒙化适配指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
React Native for OpenHarmony 实战:三方库 react-native-action-button 的鸿蒙化适配指南

本文记录把react-native-action-button(可展开的悬浮操作按钮 FAB)适配到 HarmonyOS 的完整过程。

它做的事:右下角一个圆形按钮,点开后沿弧线/直线弹出若干操作项,每项带标题与图标。

结论:它在本环境里"import 就会崩",但崩溃点只有一行、而且只需一个 20 行的 polyfill 就能修好。
修好之后,29 项契约里 28 项 defaultProps 常量全部精确命中,两个操作项的标题正常渲染,ref也能调用它的reset()。
但有一处修不了的语义变化:它依赖的componentWillReceiveProps在 React 19 里已被移除 ⇒受控模式失效。

★★★★★ 一、它的崩溃点:一行Text.propTypes.style

ActionButton.jsL366(在模块顶层求值的propTypes对象里):

ActionButton.propTypes={…buttonTextStyle:Text.propTypes.style,// ★ L366…};

而 RN 从 0.62 起就移除了Text.propTypes—— 我在 RN 0.84 上确证过:

node_modules/react-native/Libraries/Text/Text.js(27541 B)里 `propTypes` 命中 = 0 Libraries/Text/*.js 里 "propTypes =" 命中 = 0 RN index.js: get Text() { return require('./Libraries/Text/Text').default; }

⇒Text.propTypes是undefined⇒undefined.style⇒ TypeError⇒库在 import 时直接崩✗

★★★★★ 修法:一个 20 行的 polyfill(不改库)

关键判断:这是一行【类型声明】,没有任何渲染逻辑用到它⇒只要让它能安全求值,库的其余部分完全不受影响:

// polyfills/text-prop-types.jsconst{Text}=require('react-native');if(!Text.propTypes){Text.propTypes={style:{}};// 只需要"存在且能取 .style"console.log('[polyfill] NOTE | 已为 RN 的 Text 补回 propTypes…');}

执行的顺序极其关键—— 必须在"库被 import 之前"跑:

// index.jsimport'./polyfills/text-prop-types';// ★ 放在所有页面 import 之前(实测第 28 行)…importActionButtonTestAppfrom'./ActionButtonTestApp';// 第 76 行,它内部才 import 那个库

ESM 的import是按书写顺序执行的(声明会提升,但求值不会)
⇒ 所以这一行在前,就能保证库被 import 时Text.propTypes已经就位 ✓

实测(hilog 里的 polyfill 自己的日志 + 页面上的判据):

10-17 15:28:13.054 I A0beef/#RNOH_JS: __█__ [polyfill] NOTE | 已为 RN 的 Text 补回 propTypes(供依赖 Text.propTypes.style 的老库使用) PASS | ★★★ polyfill 生效:Text.propTypes 已存在 | 期望=object | 实际=object PASS | ★★★ polyfill 生效:Text.propTypes.style 已存在 | 期望=object | 实际=object PASS | ★ 默认导出 ActionButton 是 class(function) PASS | ★ ActionButtonItem 是 class(function)

⇒⇒ 补上之后库正常加载、正常渲染✓✓

⚠️这是"兼容补丁",不是 RN 的正确用法。
判断它够不够用,关键看那个库是只在propTypes声明里用Text.propTypes,
还是在渲染路径上用 —— 前者一行补丁就够,后者就要单独评估。

★★★★ 二、修不了的那一处:componentWillReceiveProps已被 React 19 移除

ActionButton.jsL42:

componentWillReceiveProps(nextProps){if(nextProps.resetToken!==this.state.resetToken){if(nextProps.active===false&&this.state.active===true){…收起…}if(nextProps.active===true&&this.state.active===false){…展开…}this.setState({resetToken:nextProps.resetToken});}}

⇒它负责"受控模式":外部用active+resetToken两个 prop 驱动按钮的展开/收起
⇒而 React 19 早已移除该生命周期⇒这段逻辑不会被调用✗

实测印证(它确实还挂在 prototype 上,但不会被执行):

NOTE | prototype 上的方法(filter 掉 constructor) = componentDidMount, componentWillUnmount, componentWillReceiveProps, getOrientation, getOffsetXY, getOverlayStyles, render, _renderMainButton, _renderButtonIcon, _renderActions, _renderTappableBackground, animateButton, reset NOTE | 实例 state 的键 = resetToken, active PASS | ★ 实例有 state.active PASS | ★★ ref 调用 reset() 不抛错

⇒⇒ 结论:

  • 受控模式(active={true}驱动)在本环境失效⚠️
  • 但"非受控模式"仍然可用✓ —— 点击触发内部的animateButton,ref也能调reset()/animateButton()✓

用受控模式怎么办?两个办法:

  1. 用ref手动驱动(inst.reset()/inst.animateButton());
  2. 给组件加key,用重挂载代替"受控更新"(<ActionButton key={String(active)} active={active} />)——
    这样active会作为初始值进入构造函数(this.anim = new Animated.Value(props.active ? 1 : 0))✓

★★★★ 三、28 项defaultProps常量逐个核对(其中一项是我数错了)

NOTE | defaultProps 的键数 = 28 NOTE | defaultProps 的键 = accessibilityLabel, accessible, active, activeOpacity, autoInactive, backdrop, backgroundTappable, bgColor, bgOpacity, buttonColor, buttonText, buttonTextStyle, degrees, fixNativeFeedbackRadius, nativeFeedbackRippleColor, offsetX, offsetY, onPress, onPressIn, onPressOn, outRangeScale, position, resetToken, size, spacing, testID, useNativeFeedback, verticalOrientation FAIL | ★★ defaultProps 恰好 29 项 | 期望=29 | 实际=28 ← ★ 我数错了 PASS | ★★ buttonText = "+" PASS | ★★ size = 56 PASS | ★ spacing = 20 PASS | ★ position = right PASS | ★ offsetX = 30 PASS | ★ offsetY = 30 PASS | ★ degrees = 45 PASS | ★ verticalOrientation = up PASS | ★ useNativeFeedback = true PASS | ★ activeOpacity = 0.85 PASS | ★ active = false PASS | ★ resetToken = null PASS | ★ backdrop = false PASS | ★ autoInactive = true PASS | ★ outRangeScale = 1 PASS | ★ bgColor = transparent PASS | ★ bgOpacity = 1

⇒18 项取值全部精确命中✓✓
⇒但"恰好 29 项"那条 FAIL 是我自己数错了(实际 28 项)✗ ——如实记录,不改判据去迁就

ActionButtonItem的defaultProps(getter 形式):

NOTE | ActionButtonItem.defaultProps 的键 = active, activeOpacity, fixNativeFeedbackRadius, nativeFeedbackRippleColor, spaceBetween, useNativeFeedback

★★★ 四、isAndroid = false在鸿蒙上是安全的

shared.js:

exportconstisAndroid=Platform.OS==="android";// 鸿蒙 ⇒ falseexportfunctiongetTouchableComponent(useNativeFeedback){if(useNativeFeedback===true&&isAndroid===true)returnTouchableNativeFeedback;returnTouchableOpacity;// ★ 鸿蒙走这里(安全)}exportfunctiontouchableBackground(color,fixRadius){if(isAndroid){…}// ★ 鸿蒙不进returnundefined;// ★ 安全}exportconstshadowStyle={shadowOpacity:.35,…,elevation:5};

⇒elevation(Android 阴影)与shadow*(iOS 阴影)都是平台专属属性⇒鸿蒙上被忽略、不崩✓
⇒⇒ 这一处"平台判断"没有造成任何问题✓

★★★ 五、文本级判据:两个操作项的标题都渲染出来了

✓ AB 项一 w=1188 h=84 x=66 y=906 ✓ AB 项二 w=1188 h=84 x=66 y=906

⇒ActionButtonItem的title确实渲染了✓✓
(ActionButton本体是绝对定位的浮层,按钮文本'+'在屏幕右下角,不在正文流里)
⇒LogBox:屏上无通知✓ —— 我原本预期会有Animated: 'useNativeDriver' was not specified警告
(因为它的Animated.spring(this.anim, {toValue: …})全都没传useNativeDriver),
但实测屏上没有出现⇒ 如实记录为"未观察到该警告" ✓


六、接入与预检(要点)

# 装库(file: 接入;它的 dependencies 只有 prop-types,宿主通常已有)npminstall"git+https://atomgit.com/oh-react-native/react-native-action-button.git#2.8.5-ohos-1.0.0"--ignore-scripts# ★ 然后必须在你入口文件的最前面加一行 polyfill(见第一节),否则 import 即崩

七、交付包质量与基线说明

类别结果
交付包内容ActionButton.js(11459 B)+ActionButtonItem.js(5414 B)+shared.js(1010 B)+index.d.ts⇒ 源码与类型都在 ✓
⚠️ 包内含example/50 个文件里大半是 example 目录(Android/iOS 工程)⇒ 开发时可能有干扰,无害
spec.json⚠️缺upstreamCommit
validation⚠️"build-pass-device-pending"
⚠️ 声明的基线reactNative: "0.88.0"+rnoh: "0.84.4"⇒两字段自相矛盾
备注库代码零改动(我加的是自己的 polyfill,没改库)

关于基线:本次验证在RN0.84.1+ RNOH0.84.3上完成。
⚠️ 这个库有两处版本相关的问题(Text.propTypes、componentWillReceiveProps),换基线时必须重新评估这两条。


八、已知限制与本次验证边界

8.1 用这个库要知道的

事项说明
★★★★★ 直接 import 会崩Text.propTypes.style(L366)在 RN ≥0.62 是undefined⇒必须加 polyfill
★★★★ 受控模式(activeprop)失效componentWillReceiveProps被 React 19 移除 ⇒ 用ref或key重挂载代替
★★★★ 28 项defaultProps全是常量适合做回归断言(buttonText '+'/size 56/spacing 20/position 'right'/offsetX 30…)
★★★ref可用的公开方法reset()、animateButton()(reset实测不抛错)
★★★isAndroid = false无害走TouchableOpacity,elevation/shadow*被忽略
★★ 两个操作项的title正常渲染文本级判据命中
★★ 未观察到useNativeDriver警告它的Animated.spring都没传该参数,但屏上没出现警告(如实记录)
★ 包里有example/目录占了大半文件数

8.2 本次验证的边界

  1. ★★★ 没有验证"点击展开"的真实交互(FAB 的展开动画、弧线排布、backdrop遮罩);
  2. ★★★ 受控模式只"证伪"未"验证替代方案"(ref调reset()/key重挂载都只是建议,未实测展开效果);
  3. ★★ 未验证展开后的几何(degrees: 45/spacing: 20/size: 56的弧线位置);
  4. ★★ 未验证renderIcon自定义图标;
  5. ★★ 未验证backdrop/backgroundTappable/autoInactive的实际行为;
  6. ★★ 未验证position='left'/verticalOrientation='down';
  7. 未验证hideShadow/ 自定义shadowStyle在鸿蒙上的视觉表现;
  8. 未验证多个ActionButton并存的层级/zIndex;
  9. 未在RN 0.88.0/RNOH 0.84.4上复验;
  10. 未测其它屏幕宽度;未做跨平台对照。

库代码未改动(只新增了我自己的 polyfill 文件)。


九、常见问题

Q1:一装就报Cannot read property 'style' of undefined?

因为ActionButton.jsL366 用了Text.propTypes.style,而RN ≥0.62 已移除Text.propTypes。
加 polyfill(见第一节):

// polyfills/text-prop-types.jsconst{Text}=require('react-native');if(!Text.propTypes)Text.propTypes={style:{}};

并在入口文件最前面(所有页面 import 之前)import './polyfills/text-prop-types';

Q2:active={true}传进去没反应?

因为驱动它的是componentWillReceiveProps,React 19 已移除该生命周期。
两个替代方案:① 用ref调inst.reset()/inst.animateButton();② 用key重挂载(让active作为初始值进构造函数)。

Q3:它在 RN 0.84 上能跑吗?

加 polyfill 之后能跑:契约 28 项defaultProps(取值 18 项精确命中)、两个操作项标题渲染、ref调reset()不抛错、LogBox无通知。
但受控模式失效(见 Q2)。

Q4:Platform.OS === "android"的判断会有问题吗?

不会。鸿蒙上isAndroid = false⇒getTouchableComponent()返回TouchableOpacity、touchableBackground()返回undefined,都安全;
elevation/shadow*这些平台专属属性会被忽略。

Q5:这种 polyfill 什么时候"够用"、什么时候"不够"?

看那个库是只在propTypes声明里用被移除的 API,还是在渲染路径上用。
前者(本库)一行补丁就够;后者补丁无效,需要改库或换库。

Q6:ref上有哪些可用方法?

实测prototype上有:componentDidMount、componentWillUnmount、componentWillReceiveProps(不会执行)、getOrientation、getOffsetXY、getOverlayStyles、render、_renderMainButton、_renderButtonIcon、_renderActions、_renderTappableBackground、animateButton、reset。
其中reset()实测可安全调用✓


小结

react-native-action-button是可展开的悬浮操作按钮,代码本身很干净(ListView/createClass/React.PropTypes/ViewPropTypes/字符串 ref/this.refs/mixins/getDefaultProps全部为空),
但它有两处与版本演进撞车的地方,一处能修、一处不能修。

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

  1. ★★★★★ 一个"可修的 import 即崩":Text.propTypes.style。
    RN ≥0.62 移除了Text.propTypes⇒ 模块顶层的Text.propTypes.style⇒ TypeError。
    但这是【一行类型声明】⇒ 一个 20 行的 polyfill(Text.propTypes = {style: {}})就能修 ✓,
    而且是"不改库"的修法✓(对比:如果被移除的是整个列表实现,那就只能否决 —— 那种情况补丁无效)

  2. ★★★★★ polyfill 的【执行顺序】是成败关键。
    ESM 的import声明会提升、求值不会⇒ 把 polyfill 放在所有页面 import之前(实测第 28 行 / 页面在 76 行),
    hilog里能看到它自己的 NOTE,页面判据也确认Text.propTypes已是object✓

  3. ★★★★ 一处修不了的:componentWillReceiveProps被 React 19 移除 ⇒ 受控模式失效。
    这不是"报错",而是逻辑静默不执行—— 更隐蔽。
    ⇒ 替代:ref手动驱动,或key重挂载(让active作为初始值进构造函数)。

  4. ★★★★ 28 项defaultProps逐个核对,18 项取值精确命中;但我把"恰好 29 项"断错了(实际 28)。
    ⇒如实记录、不改判据去迁就—— 契约型断言的价值就在于它会同时暴露"库变了"和"我数错了"✓

  5. ★★★Platform.OS === "android"这类判断在鸿蒙上是安全的:
    isAndroid = false⇒ 走TouchableOpacity、touchableBackground返回undefined;elevation/shadow*被忽略 ✓

  6. ★★ 别把"预期会出现的警告"写成结论。
    我本来预期有Animated: 'useNativeDriver' was not specified(它的Animated.spring确实都没传),
    但屏上没出现⇒ 只能写"未观察到",不能写"没有" ✓

  7. ★ 包里有example/目录(50 个文件里大半是它)—— 排查时要记得"这些不是库本体" ✓


本篇用到的库

项内容
三方库react-native-action-button(上游2.8.5的鸿蒙适配版)
适配仓库https://atomgit.com/oh-react-native/react-native-action-button
适配 TAG2.8.5-ohos-1.0.0
需要 HAR / 权限 / ohpm都不需要(纯 JS)
库类型可展开的悬浮操作按钮(FAB)
运行时依赖prop-types(宿主通常已有)
⚠️ 必须的额外步骤入口最前面加 polyfill 补Text.propTypes(否则 import 即崩)
导出形态默认导出ActionButton/ActionButtonItem
许可证MIT
上游仓库https://github.com/mastermoo/react-native-action-button
宿主工程RNOH084Demo(测试页rnAppKey = ActionButtonTestApp)
npminstall"git+https://atomgit.com/oh-react-native/react-native-action-button.git#2.8.5-ohos-1.0.0"--ignore-scripts
// ① 先放这个文件:polyfills/text-prop-types.jsconst{Text}=require('react-native');if(!Text.propTypes)Text.propTypes={style:{}};// ② 入口文件最前面(在所有页面 import 之前)import'./polyfills/text-prop-types';
import ActionButton from 'react-native-action-button'; import ActionButtonItem from 'react-native-action-button/ActionButtonItem'; // 非受控模式(推荐:点击自己展开/收起) <ActionButton buttonColor="#0b4f9e" position="right" offsetX={30} offsetY={90}> <ActionButtonItem title="拍照" onPress={() => {}} /> <ActionButtonItem title="相册" onPress={() => {}} /> </ActionButton> // 受控模式(active prop)在 React 19 上【失效】⇒ 改用 ref 或 key const ref = useRef(null); ref.current?.reset(); // 收起 // ⚠️ 不加 polyfill 会 import 即崩:Text.propTypes.style(L366) // ⚠️ componentWillReceiveProps 已被 React 19 移除 ⇒ 受控模式静默失效 // ⚠️ Animated.spring 都没传 useNativeDriver(实测屏上未出现警告) // ⚠️ 28 项 defaultProps:buttonText '+'、size 56、spacing 20、position 'right'、offsetX/Y 30、degrees 45
# 换页启动测试页 —— force-stop 不能省hdc shell aa force-stop com.rnoh084.demo hdc shell aa start-bcom.rnoh084.demo-aEntryAbility--psrnAppKey ActionButtonTestApp

判据的复现方式

# ① polyfill 是否执行(hilog 里它自己会打一行 NOTE)hdc shell"hilog -x | grep polyfill"# ② 契约:28 项 defaultProps + Text.propTypes 是否已补hdc shell"hilog -x | grep abc-test"|Select-String'PASS|FAIL|SUMMARY|NOTE'# ③ 文本级:两个操作项的 titledevecocli ui layout|Select-String'AB 项'

验证环境

项版本
React Native0.84.1
React19.2.3
RNOH(npm / ohpm)@react-native-oh/react-native-harmony/@rnoh/react-native-openharmony0.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
宿主依赖prop-types(已有)+必须加Text.propTypespolyfill
宿主 HAP 产物entry-default-signed.hap
本次构建assembleHap3 分 40 秒⇒ 一次成功(打包与构建退出码都检查了)
验证规模契约判据 22 条(21 PASS + 1 条是我自己把 28 数成 29);polyfill 生效双证(hilog + 页面);两个操作项标题命中;ref调reset()不抛错;LogBox 无通知;4 张截图互异

⚠️基线说明:交付包spec.json声明RN 0.88.0 + RNOH 0.84.4(两字段自相矛盾),本次验证在RN 0.84.1 + RNOH 0.84.3上完成。
⚠️ 本库有两处版本相关问题(Text.propTypes/componentWillReceiveProps)⇒换基线时必须重新评估这两条。


相关社区与项目地址

项目地址
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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 2:13:15

仍可外挂实现:给solomd添加标签管理相关功能

主要是打开标签太多的时候&#xff0c;当前关闭是一个难题&#xff01;需要逐个确认弹窗&#xff01;&#xff01; 所以我增加了全部关闭&#xff08;保存或不保存&#xff09;选项 此外&#xff0c;对在资源管理器中显示当前文件的标签命令&#xff0c;做了强化&#xff1a;显…

作者头像 李华
网站建设 2026/10/19 2:12:19

40篇短文串联高考英语3500词:附词汇表、音频视频与语法专项汇总

资源概览 这份资料是一套针对高考英语词汇的记忆资源合集&#xff0c;围绕「高中英语3500词」这一核心主题整理而成。资料的核心记忆方法是通过40篇短文将全部3500个词汇串联起来&#xff0c;帮助学习者在语境中理解和记忆单词&#xff0c;而非孤立背诵词表。 相关资料 查看文…

作者头像 李华
网站建设 2026/10/19 2:12:16

质序:完全离线的 GJB 质量体系助手技术方案

质序&#xff1a;完全离线的 GJB 质量体系助手技术方案 前言 军工质量体系工作经常被三个约束包围&#xff1a;标准条款多、材料链条长、运行环境经常断网或隔离。云端大模型在这种场景里并不总是可用&#xff0c;也不一定适合直接处理专业判断。 质序是我们围绕这些约束做的本…

作者头像 李华
网站建设 2026/10/19 2:11:50

华为2288H V5服务器不开机 卡888、iBMC进不去,到底是什么原因?

华为2288H V5服务器&#xff08;主板料号 H22H-05&#xff09;维修案例分享摘要&#xff1a;华为 2288H V5 服务器&#xff08;H22H-05 主板&#xff09;出现开机后面板数码管常显 “888”、主机无法点亮、iBMC 无法访问的硬件故障。经芯片级检测与修复&#xff0c;定位为 V_VC…

作者头像 李华
网站建设 2026/10/19 2:11:27

Binder双向通信

Binder 是双向的&#xff1a;客户端能调服务端&#xff0c;服务端也能调客户端。修改之前的demo测试下。新建一个ICallback的aidl文件&#xff1a;修改 IMyAidlInterface.aidl&#xff0c;加上注册callback和解注册callback的接口&#xff1a;构建&#xff0c;让 AIDL 重新生成…

作者头像 李华