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

React表单验证优化:按需显示错误消息的实现方案

Fixing React Form Validation: Show Errors Only for Touched Fields (Until Submit)

Hey there! I see exactly what's going on here. The issue is that your validate() function checks all fields every time it's called (on blur of any field), so even if the user hasn't touched the second field yet, it's still setting the required error for it. Let's fix this by tracking which fields the user has interacted with, and only showing errors for those fields until they hit Submit.

Key Changes Needed:

  1. Add a touched state to track which fields the user has focused/blurred
  2. Update blur handlers to mark individual fields as touched instead of running full validation immediately
  3. Adjust error display logic to only show errors if:
    • The field has been touched, OR
    • The user has tried to submit the form
  4. On submit, trigger a flag to show all errors regardless of touch status

Fixed Code

JavaScript

const ErrorOutput = ({ error }) => <span style={{ color: 'red' }}>{error}</span>;

class FormItem extends React.Component {
  render() {
    const { label, input, error, shouldShowError } = this.props;
    return (
      <div style={{ marginBottom: '12px' }}>
        <label style={{ display: 'block', marginBottom: '4px' }}>{label}</label>
        <input {...input} style={{ padding: '6px' }} />
        {shouldShowError && error && <ErrorOutput error={error} />}
      </div>
    );
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: '',
      telNo: '',
      submitAttempted: false,
      errors: {
        firstName: '',
        telNo: '',
      },
      touched: {
        firstName: false,
        telNo: false,
      },
    };
  }

  handleSubmit(e) {
    e.preventDefault();
    // Trigger full error display on submit
    this.setState(
      { submitAttempted: true },
      () => {
        const hasErrors = this.validate();
        if (!hasErrors) {
          console.log('Success! Form is valid');
          // Handle your valid submission logic here
        } else {
          console.log('Error! Fix the fields below');
        }
      }
    );
  }

  handleBlur = (fieldName) => {
    // Mark the field as touched when user leaves it
    this.setState(
      prevState => ({
        touched: { ...prevState.touched, [fieldName]: true }
      }),
      () => this.validate()
    );
  };

  validate = () => {
    const { firstName, telNo } = this.state;
    const errors = {};
    let hasErrors = false;

    // Validate first name
    if (firstName === '') {
      errors.firstName = 'First name is required';
      hasErrors = true;
    } else if (!firstName.match(/^[a-zA-Z]+$/)) {
      errors.firstName = 'Letters only';
      hasErrors = true;
    }

    // Validate phone number
    if (telNo === '') {
      errors.telNo = 'Phone number is required';
      hasErrors = true;
    } else if (!telNo.match(/^[0-9]+$/)) {
      errors.telNo = 'Numbers only';
      hasErrors = true;
    }

    this.setState({ errors });
    return hasErrors;
  };

  render() {
    const { firstName, telNo, errors, touched, submitAttempted } = this.state;
    return (
      <form onSubmit={this.handleSubmit.bind(this)} style={{ padding: '20px', maxWidth: '300px' }}>
        <FormItem
          label='First name:'
          input={{
            type: 'text',
            name: 'firstName',
            value: firstName,
            onChange: e => this.setState({ firstName: e.target.value }),
            onBlur: () => this.handleBlur('firstName'),
          }}
          error={errors.firstName}
          shouldShowError={touched.firstName || submitAttempted}
        />
        <FormItem
          label='Phone number:'
          input={{
            type: 'tel',
            name: 'telNo',
            value: telNo,
            onChange: e => this.setState({ telNo: e.target.value }),
            onBlur: () => this.handleBlur('telNo'),
          }}
          error={errors.telNo}
          shouldShowError={touched.telNo || submitAttempted}
        />
        <button type='submit' style={{ padding: '8px 16px' }}>Submit</button>
      </form>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));

HTML

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<body>
  <div id="root"></div>
</body>

What Changed?

  • touched State: This object tracks which fields the user has interacted with. We update it in handleBlur instead of running full validation right away.
  • submitAttempted Flag: When the user clicks Submit, we flip this flag to true—this forces all errors to display, even if the user never touched a field.
  • Conditional Error Display: The FormItem now uses a shouldShowError prop that checks if the field was touched or the form was submitted. Errors only render if this condition is met.
  • Targeted Blur Handling: Instead of validating every field on every blur, we first mark the specific field as touched, then run validation. This ensures only the touched field's error (if any) shows up during input.

This setup gives you exactly the behavior you wanted:

  1. Input Phase: Errors only appear for fields the user has already touched (after they blur the field).
  2. Post-Submit: All invalid fields show their errors, even if the user never interacted with them.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 06:49:23