技术文档中心
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
首页
React
Vue
TypeScript
Kotlin
React Native
Electron
Android
  • 入门基础

    • React 学习指南
    • React 快速入门
    • 状态管理基础
    • Hooks 基础
    • 组件通信
    • 生命周期与副作用
    • 实战项目
  • 进阶提升

    • Hooks 进阶
    • 组件设计模式
    • 性能优化
    • React Router
    • 表单处理
    • HTTP 请求
  • 状态管理

    • Context API
    • Redux 状态管理
    • Zustand 轻量状态管理
  • 高级主题

    • React + TypeScript
    • React 测试
    • 服务端渲染 (SSR)
    • 微前端架构

表单处理

受控组件

基础表单

import { useState } from 'react';

function LoginForm() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({ username, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
        placeholder="用户名"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="密码"
      />
      <button type="submit">登录</button>
    </form>
  );
}

多字段表单

function UserForm() {
  const [form, setForm] = useState({
    name: '',
    email: '',
    age: '',
    gender: 'male'
  });

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

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(form);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="name"
        value={form.name}
        onChange={handleChange}
        placeholder="姓名"
      />
      <input
        name="email"
        type="email"
        value={form.email}
        onChange={handleChange}
        placeholder="邮箱"
      />
      <input
        name="age"
        type="number"
        value={form.age}
        onChange={handleChange}
        placeholder="年龄"
      />
      <select name="gender" value={form.gender} onChange={handleChange}>
        <option value="male">男</option>
        <option value="female">女</option>
      </select>
      <button type="submit">提交</button>
    </form>
  );
}

表单验证

手动验证

function RegisterForm() {
  const [form, setForm] = useState({
    username: '',
    email: '',
    password: '',
    confirmPassword: ''
  });
  const [errors, setErrors] = useState({});

  const validate = () => {
    const newErrors = {};

    if (!form.username) {
      newErrors.username = '用户名不能为空';
    } else if (form.username.length < 3) {
      newErrors.username = '用户名至少3个字符';
    }

    if (!form.email) {
      newErrors.email = '邮箱不能为空';
    } else if (!/\S+@\S+\.\S+/.test(form.email)) {
      newErrors.email = '邮箱格式不正确';
    }

    if (!form.password) {
      newErrors.password = '密码不能为空';
    } else if (form.password.length < 6) {
      newErrors.password = '密码至少6个字符';
    }

    if (form.password !== form.confirmPassword) {
      newErrors.confirmPassword = '两次密码不一致';
    }

    return newErrors;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const newErrors = validate();
    
    if (Object.keys(newErrors).length === 0) {
      console.log('提交成功', form);
    } else {
      setErrors(newErrors);
    }
  };

  const handleChange = (e) => {
    const { name, value } = e.target;
    setForm(prev => ({ ...prev, [name]: value }));
    // 清除该字段的错误
    if (errors[name]) {
      setErrors(prev => ({ ...prev, [name]: '' }));
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          name="username"
          value={form.username}
          onChange={handleChange}
          placeholder="用户名"
        />
        {errors.username && <span className="error">{errors.username}</span>}
      </div>
      
      <div>
        <input
          name="email"
          type="email"
          value={form.email}
          onChange={handleChange}
          placeholder="邮箱"
        />
        {errors.email && <span className="error">{errors.email}</span>}
      </div>
      
      <div>
        <input
          name="password"
          type="password"
          value={form.password}
          onChange={handleChange}
          placeholder="密码"
        />
        {errors.password && <span className="error">{errors.password}</span>}
      </div>
      
      <div>
        <input
          name="confirmPassword"
          type="password"
          value={form.confirmPassword}
          onChange={handleChange}
          placeholder="确认密码"
        />
        {errors.confirmPassword && <span className="error">{errors.confirmPassword}</span>}
      </div>
      
      <button type="submit">注册</button>
    </form>
  );
}

React Hook Form

安装

yarn add react-hook-form

基础使用

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

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

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('username', {
          required: '用户名不能为空',
          minLength: { value: 3, message: '至少3个字符' }
        })}
        placeholder="用户名"
      />
      {errors.username && <span>{errors.username.message}</span>}

      <input
        type="password"
        {...register('password', {
          required: '密码不能为空',
          minLength: { value: 6, message: '至少6个字符' }
        })}
        placeholder="密码"
      />
      {errors.password && <span>{errors.password.message}</span>}

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

复杂验证

function RegisterForm() {
  const { register, handleSubmit, watch, formState: { errors } } = useForm();
  const password = watch('password');

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('email', {
          required: '邮箱不能为空',
          pattern: {
            value: /\S+@\S+\.\S+/,
            message: '邮箱格式不正确'
          }
        })}
        placeholder="邮箱"
      />
      {errors.email && <span>{errors.email.message}</span>}

      <input
        type="password"
        {...register('password', {
          required: '密码不能为空',
          minLength: { value: 6, message: '至少6个字符' },
          pattern: {
            value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
            message: '必须包含大小写字母和数字'
          }
        })}
        placeholder="密码"
      />
      {errors.password && <span>{errors.password.message}</span>}

      <input
        type="password"
        {...register('confirmPassword', {
          required: '请确认密码',
          validate: value => value === password || '两次密码不一致'
        })}
        placeholder="确认密码"
      />
      {errors.confirmPassword && <span>{errors.confirmPassword.message}</span>}

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

默认值

function EditForm() {
  const { register, handleSubmit } = useForm({
    defaultValues: {
      username: 'John',
      email: 'john@example.com',
      age: 25
    }
  });

  return (
    <form onSubmit={handleSubmit(console.log)}>
      <input {...register('username')} />
      <input {...register('email')} />
      <input type="number" {...register('age')} />
      <button type="submit">更新</button>
    </form>
  );
}

Formik

安装

yarn add formik

基础使用

import { Formik, Form, Field, ErrorMessage } from 'formik';

function LoginForm() {
  const initialValues = {
    username: '',
    password: ''
  };

  const validate = (values) => {
    const errors = {};
    
    if (!values.username) {
      errors.username = '用户名不能为空';
    }
    
    if (!values.password) {
      errors.password = '密码不能为空';
    } else if (values.password.length < 6) {
      errors.password = '密码至少6个字符';
    }
    
    return errors;
  };

  const onSubmit = (values, { setSubmitting }) => {
    console.log(values);
    setSubmitting(false);
  };

  return (
    <Formik
      initialValues={initialValues}
      validate={validate}
      onSubmit={onSubmit}
    >
      {({ isSubmitting }) => (
        <Form>
          <Field name="username" placeholder="用户名" />
          <ErrorMessage name="username" component="div" />

          <Field type="password" name="password" placeholder="密码" />
          <ErrorMessage name="password" component="div" />

          <button type="submit" disabled={isSubmitting}>
            登录
          </button>
        </Form>
      )}
    </Formik>
  );
}

Yup 验证

yarn add yup
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';

const validationSchema = Yup.object({
  username: Yup.string()
    .min(3, '至少3个字符')
    .required('用户名不能为空'),
  email: Yup.string()
    .email('邮箱格式不正确')
    .required('邮箱不能为空'),
  password: Yup.string()
    .min(6, '至少6个字符')
    .required('密码不能为空'),
  confirmPassword: Yup.string()
    .oneOf([Yup.ref('password')], '两次密码不一致')
    .required('请确认密码')
});

function RegisterForm() {
  return (
    <Formik
      initialValues={{
        username: '',
        email: '',
        password: '',
        confirmPassword: ''
      }}
      validationSchema={validationSchema}
      onSubmit={(values) => console.log(values)}
    >
      <Form>
        <Field name="username" placeholder="用户名" />
        <ErrorMessage name="username" component="div" />

        <Field name="email" placeholder="邮箱" />
        <ErrorMessage name="email" component="div" />

        <Field type="password" name="password" placeholder="密码" />
        <ErrorMessage name="password" component="div" />

        <Field type="password" name="confirmPassword" placeholder="确认密码" />
        <ErrorMessage name="confirmPassword" component="div" />

        <button type="submit">注册</button>
      </Form>
    </Formik>
  );
}

文件上传

function FileUpload() {
  const [file, setFile] = useState(null);
  const [preview, setPreview] = useState('');

  const handleChange = (e) => {
    const selectedFile = e.target.files[0];
    setFile(selectedFile);
    
    // 预览图片
    if (selectedFile && selectedFile.type.startsWith('image/')) {
      const reader = new FileReader();
      reader.onloadend = () => {
        setPreview(reader.result);
      };
      reader.readAsDataURL(selectedFile);
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    
    const formData = new FormData();
    formData.append('file', file);
    
    try {
      const response = await fetch('/api/upload', {
        method: 'POST',
        body: formData
      });
      const data = await response.json();
      console.log('上传成功', data);
    } catch (error) {
      console.error('上传失败', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="file" onChange={handleChange} accept="image/*" />
      {preview && <img src={preview} alt="预览" style={{ width: 200 }} />}
      <button type="submit" disabled={!file}>上传</button>
    </form>
  );
}

最佳实践

  1. 优先使用受控组件
  2. 使用表单库简化开发
  3. 实时验证提升用户体验
  4. 合理使用防抖优化性能
  5. 统一错误提示样式
  6. 表单提交时禁用按钮
最近更新: 2026/2/6 15:39
Contributors: hailong
Prev
React Router
Next
HTTP 请求