React Material UI:通过onKeyUp实现DOB与电话输入掩码及防字母输入
解决Material UI TextField数字字段允许输入字母的问题
我来帮你搞定这个问题!你的表单已经实现了基础的掩码格式化,但没限制输入字符类型,所以才会出现能输入字母的情况。下面提供两种实用方案,既能保留自动格式化功能,又能彻底阻止非数字输入:
方案一:手动限制输入字符(无需额外依赖)
我们可以通过onKeyPress事件拦截非数字输入,同时完善原有的掩码逻辑(注意要把DOB的分隔符统一成-,和你的验证正则匹配)。修改后的代码如下:
import React from "react"; import { Paper, withStyles, Grid, TextField, Button, FormControlLabel, Checkbox } from "@material-ui/core"; import { Face, Fingerprint } from "@material-ui/icons"; import { useForm } from "react-hook-form"; const styles = theme => ({ margin: { margin: theme.spacing.unit * 2 }, padding: { padding: theme.spacing.unit } }); const LoginTab = () => { const { handleSubmit, register, errors, reset } = useForm({ mode: "onChange" }); const onSubmit = values => { alert(JSON.stringify(values)); }; // 阻止非数字输入的通用函数 const preventNonNumericInput = (e) => { if (!e.key.match(/[0-9]/)) { e.preventDefault(); } }; return ( <form onSubmit={handleSubmit(onSubmit)}> <Grid container spacing={8} alignItems="flex-end"> <Grid item> <Face /> </Grid> <Grid item md={true} sm={true} xs={true}> <TextField id="dob" label="DOB" name="dob" // 阻止非数字输入 onKeyPress={preventNonNumericInput} onKeyUp={e => { let v = e.target.value.replace(/\D/g, ""); // 先移除所有非数字字符 if (v.length >= 4 && v.length < 6) { e.target.value = `${v.slice(0,4)}-${v.slice(4)}`; } else if (v.length >=6) { e.target.value = `${v.slice(0,4)}-${v.slice(4,6)}-${v.slice(6,10)}`; } else { e.target.value = v; } }} inputRef={register({ required: "FIELD_REQUIRED_MESSAGE", pattern: { value: /^\d{4}-\d{2}-\d{2}$/, message: "Please enter a correct DOB pattern (YYYY-MM-DD)." } })} /> {errors.dob && <p>{errors.dob.message}</p>} </Grid> </Grid> <Grid container spacing={8} alignItems="flex-end"> <Grid item md={true} sm={true} xs={true}> <TextField id="username" label="PHONE" name="phone" // 阻止非数字输入 onKeyPress={preventNonNumericInput} onKeyUp={e => { let x = e.target.value .replace(/\D/g, "") .match(/(\d{0,3})(\d{0,3})(\d{0,4})/); e.target.value = !x[2] ? x[1] : "(" + x[1] + ") " + x[2] + (x[3] ? "-" + x[3] : ""); }} inputRef={register({ required: "FIELD_REQUIRED_MESSAGE", pattern: { value: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/, message: "PHONE_VALIDATION_PATTERN_MESSAGE" } })} /> {errors.phone && <p>{errors.phone.message}</p>} </Grid> </Grid> <Grid container alignItems="center" justify="space-between"> <Grid item> <FormControlLabel control={<Checkbox color="primary" />} label="Remember me" /> </Grid> <Grid item> <Button disableFocusRipple disableRipple style={{ textTransform: "none" }} variant="text" color="primary"> Forgot password ? </Button> </Grid> </Grid> <Grid container justify="center" style={{ marginTop: "10px" }}> <Button variant="outlined" color="primary" type="submit" style={{ textTransform: "none" }}> Login </Button> </Grid> </form> ); }; export default withStyles(styles)(LoginTab);
关键改进点:
- 新增
preventNonNumericInput函数,在onKeyPress时拦截所有非数字按键,从源头阻止字母输入 - 优化DOB的掩码逻辑:先移除所有非数字字符,再按
YYYY-MM-DD格式拼接,避免原逻辑中分隔符混乱的问题 - 调整DOB的验证正则为
/^\d{4}-\d{2}-\d{2}$/,和格式化后的格式完全匹配
方案二:使用现成的输入掩码库(更简洁稳定)
如果不想手动处理正则和输入拦截,可以使用react-input-mask库,它能轻松集成Material UI TextField,并且自带输入限制功能:
- 首先安装依赖:
npm install react-input-mask --save
- 修改代码集成:
import React from "react"; import { Paper, withStyles, Grid, TextField, Button, FormControlLabel, Checkbox } from "@material-ui/core"; import { Face, Fingerprint } from "@material-ui/icons"; import { useForm } from "react-hook-form"; import InputMask from "react-input-mask"; const styles = theme => ({ margin: { margin: theme.spacing.unit * 2 }, padding: { padding: theme.spacing.unit } }); const LoginTab = () => { const { handleSubmit, register, errors, reset } = useForm({ mode: "onChange" }); const onSubmit = values => { alert(JSON.stringify(values)); }; return ( <form onSubmit={handleSubmit(onSubmit)}> <Grid container spacing={8} alignItems="flex-end"> <Grid item> <Face /> </Grid> <Grid item md={true} sm={true} xs={true}> <InputMask mask="9999-99-99" maskChar="" render={(ref, props) => ( <TextField {...props} inputRef={ref} id="dob" label="DOB" name="dob" inputRef={register({ required: "FIELD_REQUIRED_MESSAGE", pattern: { value: /^\d{4}-\d{2}-\d{2}$/, message: "Please enter a correct DOB pattern (YYYY-MM-DD)." } })} /> )} /> {errors.dob && <p>{errors.dob.message}</p>} </Grid> </Grid> <Grid container spacing={8} alignItems="flex-end"> <Grid item md={true} sm={true} xs={true}> <InputMask mask="(999) 999-9999" maskChar="" render={(ref, props) => ( <TextField {...props} inputRef={ref} id="username" label="PHONE" name="phone" inputRef={register({ required: "FIELD_REQUIRED_MESSAGE", pattern: { value: /^\(?([0-9]{3})\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})$/, message: "PHONE_VALIDATION_PATTERN_MESSAGE" } })} /> )} /> {errors.phone && <p>{errors.phone.message}</p>} </Grid> </Grid> {/* 其余表单部分保持不变 */} <Grid container alignItems="center" justify="space-between"> <Grid item> <FormControlLabel control={<Checkbox color="primary" />} label="Remember me" /> </Grid> <Grid item> <Button disableFocusRipple disableRipple style={{ textTransform: "none" }} variant="text" color="primary"> Forgot password ? </Button> </Grid> </Grid> <Grid container justify="center" style={{ marginTop: "10px" }}> <Button variant="outlined" color="primary" type="submit" style={{ textTransform: "none" }}> Login </Button> </Grid> </form> ); }; export default withStyles(styles)(LoginTab);
方案优势:
- 无需手动编写复杂的正则和输入拦截逻辑,库已经帮你处理好了
- 掩码格式直观,
mask="9999-99-99"就代表只允许输入数字,自动添加分隔符 - 稳定性更高,能处理各种边缘情况(比如删除字符、粘贴内容等)
你可以根据自己的需求选择方案,两种都能解决你当前遇到的数字字段允许输入字母的问题!
内容的提问来源于stack exchange,提问作者SDK
相关产品推荐
相关产品推荐

