React Native for Harmony 数字验证码输入功能
核心 API / 组件 / Hook作用说明鸿蒙端特性TextInput验证码输入框(单字符分箱)支持鸿蒙端numeric键盘、光标自定义,无输入延迟useState管理每个输入框的内容、聚焦状态响应式更新,鸿蒙端无重渲染卡顿useRef管理输入框引用,实现自动聚焦切换鸿蒙端需延迟执行(避坑点)Keyboard控制键盘收起 / 弹出鸿蒙端原生支持,无延迟onKeyPress监听删除键事件,实现删除
目录
一、核心知识点:数字验证码输入 完整技术体系
1、核心内置 API / 组件 / Hook 介绍
本次实现鸿蒙端数字验证码输入,全部基于 RN 原生能力(无第三方库),完美适配鸿蒙手机端的输入交互,核心能力如下:
| 核心 API / 组件 / Hook | 作用说明 | 鸿蒙端特性 |
|---|---|---|
TextInput |
验证码输入框(单字符分箱) | 支持鸿蒙端numeric键盘、光标自定义,无输入延迟 |
useState |
管理每个输入框的内容、聚焦状态 | 响应式更新,鸿蒙端无重渲染卡顿 |
useRef |
管理输入框引用,实现自动聚焦切换 | 鸿蒙端ref.current.focus()需延迟执行(避坑点) |
Keyboard |
控制键盘收起 / 弹出 | 鸿蒙端Keyboard.dismiss()原生支持,无延迟 |
onKeyPress |
监听删除键事件,实现删除后聚焦上一输入框 | 鸿蒙端需监听backspace键值,适配原生键盘 |
2、鸿蒙端验证码输入 核心实现原则
遵循「分箱输入、自动流转、交互贴合、鸿蒙适配」四大原则,是企业级鸿蒙 RN 项目的标准方案:
- 分箱输入:将验证码拆分为 N 个独立输入框(通常 4-6 个),每个输入框仅允许输入 1 位数字;
- 自动流转:输入完成当前框后,自动聚焦下一个输入框;删除当前框内容后,自动聚焦上一个输入框;
- 输入限制:强制限制数字输入、禁止特殊字符,鸿蒙端键盘默认唤起数字键盘;
- 鸿蒙交互:贴合鸿蒙端输入习惯(如光标样式、输入框高亮、键盘收起逻辑)。
3、鸿蒙官方验证码输入 设计 & 交互规范
鸿蒙系统对验证码输入组件有明确的视觉 / 交互规范,遵循后体验与原生应用一致:
- 样式规范:输入框宽度
48px、高度56px,圆角8px,未输入时边框为#E5E5E5,聚焦时为鸿蒙主题蓝#007DFF; - 交互规范:输入框默认自动聚焦第一个,输入后自动跳转到下一个,最后一个输入完成后自动收起键盘;
- 适配规范:鸿蒙手机端输入框间距
12px,平板端间距16px,光标颜色与输入框聚焦边框色一致。
二、实战:鸿蒙手机端 数字验证码输入组件
import React, { useState, useRef, useCallback } from 'react';
import {
View, TextInput, StyleSheet, SafeAreaView,
Keyboard, TextInputProps, Dimensions, Text, StyleProp, TextStyle
} from 'react-native';
const { width } = Dimensions.get('window');
const CODE_BOX_COUNT = 4;
const CODE_BOX_WIDTH = 48;
const CODE_BOX_SPACING = 12;
type CodeInputRef = React.RefObject<TextInput>;
type CodeValue = string[];
const HarmonyCodeInput: React.FC = () => {
const [codeValue, setCodeValue] = useState<CodeValue>(Array(CODE_BOX_COUNT).fill(''));
const inputRefs = useRef<CodeInputRef[]>(
Array.from({ length: CODE_BOX_COUNT }).map(() => useRef<TextInput>(null))
);
const handleInputChange = useCallback((index: number, value: string) => {
const numericValue = value.replace(/[^0-9]/g, '');
if (numericValue.length > 1) return;
const newCodeValue = [...codeValue];
newCodeValue[index] = numericValue;
setCodeValue(newCodeValue);
if (numericValue && index < CODE_BOX_COUNT - 1) {
setTimeout(() => {
inputRefs.current[index + 1]?.current?.focus();
}, 0);
}
if (index === CODE_BOX_COUNT - 1 && numericValue) {
Keyboard.dismiss();
}
}, [codeValue]);
const handleKeyPress = useCallback((index: number, e: { nativeEvent: { key: string } }) => {
if (e.nativeEvent.key === 'Backspace' && !codeValue[index]) {
if (index > 0) {
setTimeout(() => {
inputRefs.current[index - 1]?.current?.focus();
}, 0);
}
}
}, [codeValue]);
const renderCodeBox = (index: number) => {
const inputProps: TextInputProps = {
style: [styles.codeInput, !!codeValue[index] && styles.codeInputFilled] as StyleProp<TextStyle>,
value: codeValue[index],
onChangeText: (value: string) => handleInputChange(index, value),
onKeyPress: (e: { nativeEvent: { key: string } }) => handleKeyPress(index, e),
keyboardType: 'numeric',
autoCapitalize: 'none' as 'none',
autoCorrect: false,
blurOnSubmit: false,
maxLength: 1,
textAlign: 'center' as 'center',
selectionColor: '#007DFF',
autoFocus: index === 0,
};
return (
<TextInput
{...inputProps}
ref={inputRefs.current[index]}
key={index}
/>
);
};
return (
<SafeAreaView style={styles.container}>
<View style={styles.titleWrap}>
<Text style={styles.title}>请输入4位数字验证码</Text>
</View>
<View style={styles.codeBoxWrap}>
{Array.from({ length: CODE_BOX_COUNT }).map((_, index) => renderCodeBox(index))}
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F5F5',
alignItems: 'center',
paddingTop: 60,
},
titleWrap: {
marginBottom: 40,
},
title: {
fontSize: 18,
color: '#333333',
fontWeight: '500',
},
codeBoxWrap: {
flexDirection: 'row',
justifyContent: 'center',
width: CODE_BOX_COUNT * (CODE_BOX_WIDTH + CODE_BOX_SPACING) - CODE_BOX_SPACING,
},
codeInput: {
width: CODE_BOX_WIDTH,
height: 56,
borderRadius: 8,
borderWidth: 1,
borderColor: '#E5E5E5',
marginRight: CODE_BOX_SPACING,
fontSize: 22,
fontWeight: '600',
color: '#333333',
},
codeInputFilled: {
borderColor: '#007DFF',
},
});
export default HarmonyCodeInput;

三、OpenHarmony6.0 专属避坑指南
以下是鸿蒙 RN 开发中实现验证码输入的高频踩坑点,全部为鸿蒙端专属问题 + 解决方案:
| 问题现象 | 问题原因 | 鸿蒙端最优解决方案 |
|---|---|---|
| 输入框无法自动聚焦下一个 | 鸿蒙端focus()需延迟执行,直接调用可能失效 |
在handleInputChange中用setTimeout(() => inputRef.current.focus(), 0) |
| 输入框可以输入非数字字符 | 未设置keyboardType: 'numeric',或未过滤输入内容 |
同时设置keyboardType和onChangeText过滤非数字(value.replace(/[^0-9]/g, '')) |
| 输入框边框样式异常(圆角不生效) | 鸿蒙端TextInput的borderRadius需配合overflow: 'hidden' |
用View包裹输入框,在 View 上设置边框 + 圆角 |
| 删除键按下后无响应(不跳转到上一个) | 未监听Backspace键,或当前输入框值判断错误 |
在onKeyPress中监听e.nativeEvent.key === 'Backspace',并判断当前输入框是否为空 |
| 输入完成后键盘不自动收起 | 未调用Keyboard.dismiss(),或调用时机错误 |
在最后一个输入框的handleInputChange中,输入完成后执行Keyboard.dismiss() |
| 输入框光标颜色与鸿蒙主题不符 | 未设置selectionColor,默认颜色与主题冲突 |
设置selectionColor: '#007DFF'(鸿蒙官方主题蓝) |
| 鸿蒙平板端输入框间距过小 / 过大 | 硬编码间距,未适配平板尺寸 | 用Dimensions动态计算间距:平板端间距设为 16px,手机端为 12px |
TS 报错inputRef.current可能为 null |
未对 ref 做非空判断,TS 类型校验严格 | 调用focus()前加可选链:inputRefs.current[index + 1]?.current?.focus() |
四、扩展用法:验证码输入 高频进阶功能
基于本次基础代码,可快速扩展鸿蒙端项目常用的验证码功能:
扩展 1:结合倒计时 “获取验证码” 按钮
在输入框上方添加 “获取验证码” 按钮,用useState管理倒计时(如 60 秒),点击后禁用按钮并开始倒计时,倒计时结束后恢复可用,同时适配鸿蒙端按钮样式规范。
扩展 2:自动填充系统验证码
结合鸿蒙端ClipboardAPI(import { Clipboard } from 'react-native'),在组件挂载后读取剪贴板内容,若为 4 位数字则自动填充到输入框中,提升用户体验。
扩展 3:验证码错误提示动画
输入错误验证码后,给输入框添加 “抖动” 动画(用Animated实现),同时将输入框边框改为红色,提示用户输入错误,符合鸿蒙端交互反馈规范。
扩展 4:深色模式适配
结合之前的useColorScheme,抽离 “浅色 / 深色” 两套验证码样式(输入框背景、边框色、文字色),动态绑定到组件中,适配鸿蒙系统的深色模式。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)