React+RHF+MUI下自定义输入掩码添加可编辑字符串求助
自定义掩码输入框解决方案
技术栈
- Js + React
- React Hook Form
- Material UI
需求目标
需要实现一个带自定义掩码的输入框,掩码结构为:
- 固定前缀
vm- - 用户可编辑的字符串(位于
vm-之后、tenant变量之前) - 来自其他输入框的动态变量
tenant(通过watch('tenant')获取) - 三位用户可编辑的整数
问题
目前自行实现了掩码逻辑,但无法将代码中固定的a替换为用户可编辑的字符串,该字符串需要放在vm-和tenant变量之间。
现有代码
import React, { useEffect } from 'react'; import { Box, TextField, Typography } from '@mui/material'; import { Controller } from 'react-hook-form'; export const Name = ({ fieldArrayName, index, setValue, watch, control }) => { const tenant = watch('tenant'); useEffect(() => { if (tenant) setValue(`${fieldArrayName}.${index}.name`, ''); }, [tenant]); const formatInputValue = (tenant, input) => { const tenantFormatted = tenant ? `vm-a${tenant}-` : 'vm-a'; const inputFormatted = input.slice(tenantFormatted.length); return `${tenantFormatted}${inputFormatted.replace(/\D/g, '').slice(0, 3)}`; }; return ( <> <Box> <Controller control={control} name={`${fieldArrayName}.${index}.name`} defaultValue="" rules={{ required: 'Name is required', pattern: { value: /^vm-(\w+)(\d{1,3})-(\d{1,3})$/, message: 'Need to be "vm-a{tenant}-999"', }, }} render={({ field: { value, onChange }, fieldState: { error } }) => ( <TextField label="Name. ex: vm-a{tenant}-100" value={value} onChange={(e) => { const formattedValue = formatInputValue(tenant, e.target.value); onChange(formattedValue); }} error={Boolean(error)} helperText={Boolean(error) && error.message} /> )} /> </Box> </> ); };
已尝试方案
曾使用react-input-mask,设置掩码为mask="vm-a{tenant}-999",但当tenant值为数字(比如999)时,会被组件的9规则(代表整数输入)替换为空,无法满足需求。
解决方案
重构formatInputValue函数,拆分输入内容的各个段,分别处理静态前缀、用户自定义字符串、tenant变量和末尾整数,实现动态适配的掩码逻辑:
import React, { useEffect } from 'react'; import { Box, TextField } from '@mui/material'; import { Controller } from 'react-hook-form'; export const Name = ({ fieldArrayName, index, setValue, watch, control }) => { const tenant = watch('tenant'); useEffect(() => { if (tenant) setValue(`${fieldArrayName}.${index}.name`, ''); }, [tenant, setValue, fieldArrayName, index]); const formatInputValue = (tenant, input) => { // 根据tenant是否存在,定义匹配掩码结构的正则 const maskRegex = tenant ? /^vm-(\w*)(.*?)-?(\d{0,3})$/ : /^vm-(\w*)(\d{0,3})$/; // 提取输入中的各部分内容 const match = input.match(maskRegex) || []; const customStr = match[1] || ''; const remaining = match[2] || ''; const digits = match[3] || ''; // 处理自定义字符串:仅保留字母、数字、下划线 const processedCustomStr = customStr.replace(/[^\w]/g, ''); // 处理末尾数字:仅保留数字,最多三位 const processedDigits = digits.replace(/\D/g, '').slice(0, 3); // 拼接最终的格式化值 if (tenant) { return `vm-${processedCustomStr}${tenant}-${processedDigits}`; } else { return `vm-${processedCustomStr}${processedDigits}`; } }; return ( <Box> <Controller control={control} name={`${fieldArrayName}.${index}.name`} defaultValue="" rules={{ required: 'Name is required', pattern: { value: tenant ? new RegExp(`^vm-\\w+${tenant}-\\d{1,3}$`) : /^vm-\w+\d{1,3}$/, message: tenant ? '格式需为 "vm-自定义字符串{tenant}-三位整数"' : '格式需为 "vm-自定义字符串三位整数"', }, }} render={({ field: { value, onChange }, fieldState: { error } }) => ( <TextField label={`Name. ex: ${tenant ? `vm-abc${tenant}-100` : 'vm-abc100'}`} value={value} onChange={(e) => { const formattedValue = formatInputValue(tenant, e.target.value); onChange(formattedValue); }} error={Boolean(error)} helperText={error?.message} /> )} /> </Box> ); };
关键说明
- 动态正则:根据
tenant是否存在切换匹配规则,适配两种输入结构 - 分段处理:将输入拆分为自定义字符串、剩余内容、末尾数字三个部分,分别做格式校验和过滤
- 动态拼接:确保
vm-、自定义字符串、tenant、末尾数字的顺序始终正确 - 适配验证:表单校验规则随
tenant状态动态调整,保证输入格式符合要求
内容的提问来源于stack exchange,提问作者Elodie
相关产品推荐
相关产品推荐

