Skip to content
📖预计阅读时长:0 分钟字数:0

React 表单处理

基础篇第 5 章:掌握 React 中的表单处理技巧。

返回 React学习路线 | 上一章 React-04-路由与导航 | 下一章 React-06-样式方案


一、受控组件

1.1 基本用法

jsx
function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({ name, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

1.2 统一状态管理

jsx
function Form() {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: '',
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value,
    }));
  };

  return (
    <form>
      <input name="name" value={formData.name} onChange={handleChange} />
      <input name="email" value={formData.email} onChange={handleChange} />
      <textarea name="message" value={formData.message} onChange={handleChange} />
    </form>
  );
}

二、不同表单元素

2.1 各类型输入

jsx
function AllInputTypes() {
  const [form, setForm] = useState({
    text: '',
    password: '',
    number: 0,
    checkbox: false,
    radio: 'option1',
    select: '',
    multiSelect: [],
    textarea: '',
  });

  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setForm(prev => ({
      ...prev,
      [name]: type === 'checkbox' ? checked : value,
    }));
  };

  return (
    <form>
      {/* 文本输入 */}
      <input type="text" name="text" value={form.text} onChange={handleChange} />

      {/* 密码 */}
      <input type="password" name="password" value={form.password} onChange={handleChange} />

      {/* 数字 */}
      <input type="number" name="number" value={form.number} onChange={handleChange} />

      {/* 复选框 */}
      <input type="checkbox" name="checkbox" checked={form.checkbox} onChange={handleChange} />

      {/* 单选框 */}
      <input type="radio" name="radio" value="option1" checked={form.radio === 'option1'} onChange={handleChange} />
      <input type="radio" name="radio" value="option2" checked={form.radio === 'option2'} onChange={handleChange} />

      {/* 下拉选择 */}
      <select name="select" value={form.select} onChange={handleChange}>
        <option value="">Select...</option>
        <option value="a">Option A</option>
        <option value="b">Option B</option>
      </select>

      {/* 多行文本 */}
      <textarea name="textarea" value={form.textarea} onChange={handleChange} />
    </form>
  );
}

2.2 多选复选框

jsx
function MultiCheckbox() {
  const [selected, setSelected] = useState([]);
  const options = ['React', 'Vue', 'Angular'];

  const handleChange = (option) => {
    setSelected(prev => 
      prev.includes(option)
        ? prev.filter(item => item !== option)
        : [...prev, option]
    );
  };

  return (
    <div>
      {options.map(option => (
        <label key={option}>
          <input
            type="checkbox"
            checked={selected.includes(option)}
            onChange={() => handleChange(option)}
          />
          {option}
        </label>
      ))}
    </div>
  );
}

三、表单验证

3.1 基础验证

jsx
function FormWithValidation() {
  const [email, setEmail] = useState('');
  const [errors, setErrors] = useState({});

  const validate = () => {
    const newErrors = {};
    
    if (!email) {
      newErrors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(email)) {
      newErrors.email = 'Email is invalid';
    }
    
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (validate()) {
      // 提交表单
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        className={errors.email ? 'error' : ''}
      />
      {errors.email && <span className="error-message">{errors.email}</span>}
      <button type="submit">Submit</button>
    </form>
  );
}

3.2 自定义 Hook

jsx
function useForm(initialValues, validate) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});
  const [touched, setTouched] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setValues(prev => ({ ...prev, [name]: value }));
  };

  const handleBlur = (e) => {
    const { name } = e.target;
    setTouched(prev => ({ ...prev, [name]: true }));
    if (validate) {
      setErrors(validate(values));
    }
  };

  const handleSubmit = (onSubmit) => (e) => {
    e.preventDefault();
    const validationErrors = validate ? validate(values) : {};
    setErrors(validationErrors);
    setTouched(Object.keys(values).reduce((acc, key) => ({ ...acc, [key]: true }), {}));
    
    if (Object.keys(validationErrors).length === 0) {
      onSubmit(values);
    }
  };

  return { values, errors, touched, handleChange, handleBlur, handleSubmit };
}

// 使用
function LoginForm() {
  const validate = (values) => {
    const errors = {};
    if (!values.email) errors.email = 'Required';
    if (!values.password) errors.password = 'Required';
    return errors;
  };

  const { values, errors, touched, handleChange, handleBlur, handleSubmit } = useForm(
    { email: '', password: '' },
    validate
  );

  return (
    <form onSubmit={handleSubmit((values) => console.log(values))}>
      <input
        name="email"
        value={values.email}
        onChange={handleChange}
        onBlur={handleBlur}
      />
      {touched.email && errors.email && <span>{errors.email}</span>}
      {/* ... */}
    </form>
  );
}

四、表单库推荐

4.1 React Hook Form

jsx
import { useForm } from 'react-hook-form';

function Form() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email', { 
        required: 'Email is required',
        pattern: {
          value: /\S+@\S+\.\S+/,
          message: 'Invalid email'
        }
      })} />
      {errors.email && <span>{errors.email.message}</span>}

      <input {...register('password', { 
        required: 'Password is required',
        minLength: {
          value: 6,
          message: 'Min 6 characters'
        }
      })} type="password" />
      {errors.password && <span>{errors.password.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

4.2 表单库对比

特点适用场景
React Hook Form性能好、体积小推荐首选
Formik功能全面复杂表单
原生实现完全控制简单表单

五、本章小结

模式适用场景
受控组件需要实时验证、联动
非受控组件简单场景、文件上传
表单库复杂表单、验证逻辑多

下一步


#React #表单 #验证 #ReactHookForm