You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,并且自带输入限制功能:

  1. 首先安装依赖:
npm install react-input-mask --save
  1. 修改代码集成:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 17:17:46