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

React函数组件表单校验器失效,输入非法仍发送API请求是什么原因?

问题原因
  • setError是异步更新方法:React 的 useState 返回的更新函数setError是异步生效的,调用setError后当前渲染周期内的error变量仍然是更新前的旧值(第一次校验时旧值为空字符串),所以后续判断error !== ''的逻辑永远拿不到刚设置的错误信息,直接进入resolve分支触发API请求。
  • 邮箱正则表达式书写错误:你定义的正则RegExp('w+@example.com')中的w会被识别为普通字符,仅能匹配以字母w开头的邮箱,而非你预期的「匹配任意数字、字母、下划线」,如果要使用元字符\w,在字符串写法里需要转义为\\w,另外正则里的.也需要转义,避免被识别为匹配任意字符的元字符,更推荐直接使用正则字面量写法降低出错概率。
  • 表单数据存储方式错误:你把data定义为组件内的普通变量,每次组件重新渲染时data都会被重置为空对象,存在数据丢失的风险,应该使用useState存储表单数据。
修复方案

直接调整校验器的逻辑,不需要依赖异步更新的error状态做判断,在校验器内部定义临时错误变量判断即可,同时修正正则和表单数据的存储方式,核心修改后的代码如下:

import React, { useState } from 'react';
import Button from '@material-ui/core/Button';
import { makeStyles } from '@material-ui/styles';
import TextField from '@material-ui/core/TextField';

const useStyles = makeStyles(() => ({
  avatar: {
    margin: '10px auto',
    backgroundColor: 'green',
  },
  paper: {
    width: '50vw',
    margin: '1rem auto 3rem auto',
    padding: '2rem',
    display: 'flex',
    flexDirection: 'column',
    backgroundColor: 'blue',
    backdropFilter: 'blur(10px)',
    
  },
  align: {
    marginLeft: 'auto',
    marginRight: 'auto',
  },
  field: {
    width: '100%',
    marginLeft: 'auto',
    marginRight: 'auto',
    marginTop: '15px',
  },
  form: {
    width: '80%',
    display: 'flex',
    flexDirection: 'column',
    margin: '1rem auto',
  },
}));


export default function Signup() {
  const classes = useStyles();
  const [error, setError] = useState('');
  // 把表单数据改为state存储,避免重渲染丢失数据
  const [data, setData] = useState({
    username: '',
    email: '',
    first_name: '',
    last_name: '',
    password: '',
    password2: ''
  });

  function updateData(event, key) {
    setData(prev => ({
      ...prev,
      [key]: event.target.value
    }));
  }

  function validator() {
    console.log(data);
    return new Promise((resolve, reject) => {
      // 修正正则为字面量写法,.增加转义
      const isEmailValid = /\w+@example\.com/.test(data.email);
      let errMsg = '';
      if (!isEmailValid) {
        errMsg = 'Email Not Valid! Please use Institute Email ID';
      }
      if (data.password !== data.password2) {
        errMsg = 'Password Does Not Match!';
      }
      // 直接用内部临时变量判断,不依赖异步更新的state
      if (errMsg !== '') {
        setError(errMsg);
        reject(errMsg);
      } else {
        resolve('ok');
      }
    });
  }

  const onSignup = (e) => {
    e.preventDefault();
    validator()
      .then(() => {
        console.log('API Request Sent');
      })
      .catch((err) => {
        // 直接使用catch返回的错误信息,不需要依赖state的error
        console.log('The errors are: '+ err)
      });
  };

  return (
    
      <form className={classes.form}>
        <TextField
          required
          className={classes.field}
          id="outlined-required"
          label="User Name"
          variant="outlined"
          onChange={(event) => updateData(event, 'username')}
        />
        <TextField
          required
          type="email"
          className={classes.field}
          id="outlined-required"
          label="Email"
          variant="outlined"
          onChange={(event) => {
            updateData(event, 'email');
          }}
        />
        <TextField
          required
          className={classes.field}
          id="outlined-required"
          label="First Name"
          variant="outlined"
          onChange={(event) => updateData(event, 'first_name')}
        />
        <TextField
          required
          className={classes.field}
          id="outlined-required"
          label="Last Name"
          variant="outlined"
          onChange={(event) => updateData(event, 'last_name')}
        />
        <TextField
          required
          type="password"
          className={classes.field}
          id="outlined-required"
          label="Password"
          variant="outlined"
          onChange={(event) => updateData(event, 'password')}
        />
        <TextField
          required
          type="password"
          className={classes.field}
          id="outlined-required"
          label="Confirm Password"
          variant="outlined"
          onChange={(event) => {
            updateData(event, 'password2');
          }}
        />
        <Button
          className={classes.field}
          color="primary"
          variant="contained"
          onClick={(e) => onSignup(e)}
        >
          Sign Up
        </Button>
      </form>
  );
}

内容的提问来源于stack exchange,提问作者Santam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 11:15:04