Form 表单组件 #

FormControl 表单控件 #

FormControl 是一个包装组件,用于为表单控件提供上下文。

jsx
import {
  FormControl,
  FormLabel,
  FormHelperText,
  FormErrorMessage,
  Input,
} from '@chakra-ui/react'

function FormExample() {
  return (
    <FormControl>
      <FormLabel>邮箱地址</FormLabel>
      <Input type="email" />
      <FormHelperText>我们不会分享您的邮箱</FormHelperText>
    </FormControl>
  )
}

表单状态 #

必填 #

jsx
<FormControl isRequired>
  <FormLabel>用户名</FormLabel>
  <Input />
</FormControl>

错误 #

jsx
<FormControl isInvalid>
  <FormLabel>邮箱</FormLabel>
  <Input />
  <FormErrorMessage>邮箱格式不正确</FormErrorMessage>
</FormControl>

禁用 #

jsx
<FormControl isDisabled>
  <FormLabel>禁用字段</FormLabel>
  <Input />
</FormControl>

只读 #

jsx
<FormControl isReadOnly>
  <FormLabel>只读字段</FormLabel>
  <Input />
</FormControl>

完整表单示例 #

登录表单 #

jsx
function LoginForm() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [errors, setErrors] = useState({})

  const validateForm = () => {
    const newErrors = {}
    if (!email) newErrors.email = '邮箱不能为空'
    if (!password) newErrors.password = '密码不能为空'
    setErrors(newErrors)
    return Object.keys(newErrors).length === 0
  }

  const handleSubmit = (e) => {
    e.preventDefault()
    if (validateForm()) {
      console.log('提交成功', { email, password })
    }
  }

  return (
    <Box as="form" onSubmit={handleSubmit} w="100%" maxW="400px">
      <VStack spacing={4}>
        <FormControl isInvalid={errors.email}>
          <FormLabel>邮箱</FormLabel>
          <Input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
          <FormErrorMessage>{errors.email}</FormErrorMessage>
        </FormControl>

        <FormControl isInvalid={errors.password}>
          <FormLabel>密码</FormLabel>
          <Input
            type="password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
          />
          <FormErrorMessage>{errors.password}</FormErrorMessage>
        </FormControl>

        <Button type="submit" colorScheme="blue" w="full">
          登录
        </Button>
      </VStack>
    </Box>
  )
}

注册表单 #

jsx
function RegisterForm() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
    confirmPassword: '',
  })
  const [errors, setErrors] = useState({})

  const handleChange = (field) => (e) => {
    setFormData({ ...formData, [field]: e.target.value })
  }

  const validateForm = () => {
    const newErrors = {}
    if (!formData.username) newErrors.username = '用户名不能为空'
    if (!formData.email) newErrors.email = '邮箱不能为空'
    if (formData.password.length < 6) newErrors.password = '密码至少 6 位'
    if (formData.password !== formData.confirmPassword) {
      newErrors.confirmPassword = '两次密码不一致'
    }
    setErrors(newErrors)
    return Object.keys(newErrors).length === 0
  }

  const handleSubmit = (e) => {
    e.preventDefault()
    if (validateForm()) {
      console.log('注册成功', formData)
    }
  }

  return (
    <Box as="form" onSubmit={handleSubmit} w="100%" maxW="400px">
      <VStack spacing={4}>
        <FormControl isInvalid={errors.username}>
          <FormLabel>用户名</FormLabel>
          <Input
            value={formData.username}
            onChange={handleChange('username')}
          />
          <FormErrorMessage>{errors.username}</FormErrorMessage>
        </FormControl>

        <FormControl isInvalid={errors.email}>
          <FormLabel>邮箱</FormLabel>
          <Input
            type="email"
            value={formData.email}
            onChange={handleChange('email')}
          />
          <FormErrorMessage>{errors.email}</FormErrorMessage>
        </FormControl>

        <FormControl isInvalid={errors.password}>
          <FormLabel>密码</FormLabel>
          <Input
            type="password"
            value={formData.password}
            onChange={handleChange('password')}
          />
          <FormErrorMessage>{errors.password}</FormErrorMessage>
        </FormControl>

        <FormControl isInvalid={errors.confirmPassword}>
          <FormLabel>确认密码</FormLabel>
          <Input
            type="password"
            value={formData.confirmPassword}
            onChange={handleChange('confirmPassword')}
          />
          <FormErrorMessage>{errors.confirmPassword}</FormErrorMessage>
        </FormControl>

        <Button type="submit" colorScheme="blue" w="full">
          注册
        </Button>
      </VStack>
    </Box>
  )
}

联系表单 #

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

  const handleSubmit = (e) => {
    e.preventDefault()
    console.log('提交', formData)
  }

  return (
    <Box as="form" onSubmit={handleSubmit} w="100%" maxW="500px">
      <VStack spacing={4}>
        <SimpleGrid columns={2} spacing={4} w="full">
          <FormControl isRequired>
            <FormLabel>姓名</FormLabel>
            <Input
              value={formData.name}
              onChange={(e) => setFormData({ ...formData, name: e.target.value })}
            />
          </FormControl>

          <FormControl isRequired>
            <FormLabel>邮箱</FormLabel>
            <Input
              type="email"
              value={formData.email}
              onChange={(e) => setFormData({ ...formData, email: e.target.value })}
            />
          </FormControl>
        </SimpleGrid>

        <FormControl isRequired>
          <FormLabel>主题</FormLabel>
          <Input
            value={formData.subject}
            onChange={(e) => setFormData({ ...formData, subject: e.target.value })}
          />
        </FormControl>

        <FormControl isRequired>
          <FormLabel>消息</FormLabel>
          <Textarea
            rows={5}
            value={formData.message}
            onChange={(e) => setFormData({ ...formData, message: e.target.value })}
          />
        </FormControl>

        <Button type="submit" colorScheme="blue" w="full">
          发送消息
        </Button>
      </VStack>
    </Box>
  )
}

设置表单 #

jsx
function SettingsForm() {
  const [settings, setSettings] = useState({
    notifications: true,
    emailUpdates: false,
    language: 'zh',
    theme: 'light',
  })

  return (
    <VStack spacing={6} align="stretch">
      <Heading size="md">通知设置</Heading>
      
      <FormControl display="flex" alignItems="center" justifyContent="space-between">
        <Box>
          <Text fontWeight="medium">推送通知</Text>
          <Text fontSize="sm" color="gray.500">接收应用推送通知</Text>
        </Box>
        <Switch
          isChecked={settings.notifications}
          onChange={(e) => setSettings({ ...settings, notifications: e.target.checked })}
        />
      </FormControl>

      <FormControl display="flex" alignItems="center" justifyContent="space-between">
        <Box>
          <Text fontWeight="medium">邮件更新</Text>
          <Text fontSize="sm" color="gray.500">接收产品更新邮件</Text>
        </Box>
        <Switch
          isChecked={settings.emailUpdates}
          onChange={(e) => setSettings({ ...settings, emailUpdates: e.target.checked })}
        />
      </FormControl>

      <Divider />

      <Heading size="md">偏好设置</Heading>

      <FormControl>
        <FormLabel>语言</FormLabel>
        <Select
          value={settings.language}
          onChange={(e) => setSettings({ ...settings, language: e.target.value })}
        >
          <option value="zh">中文</option>
          <option value="en">English</option>
          <option value="ja">日本語</option>
        </Select>
      </FormControl>

      <FormControl>
        <FormLabel>主题</FormLabel>
        <RadioGroup
          value={settings.theme}
          onChange={(value) => setSettings({ ...settings, theme: value })}
        >
          <HStack spacing={4}>
            <Radio value="light">浅色</Radio>
            <Radio value="dark">深色</Radio>
            <Radio value="system">跟随系统</Radio>
          </HStack>
        </RadioGroup>
      </FormControl>

      <Button colorScheme="blue" alignSelf="flex-start">
        保存设置
      </Button>
    </VStack>
  )
}

表单布局技巧 #

水平布局 #

jsx
<FormControl as={HStack} spacing={4}>
  <FormLabel w="120px">用户名</FormLabel>
  <Input flex="1" />
</FormControl>

网格布局 #

jsx
<SimpleGrid columns={2} spacing={4}>
  <FormControl>
    <FormLabel>姓</FormLabel>
    <Input />
  </FormControl>
  <FormControl>
    <FormLabel>名</FormLabel>
    <Input />
  </FormControl>
</SimpleGrid>

分组表单 #

jsx
<VStack spacing={8} align="stretch">
  <Box>
    <Heading size="sm" mb={4}>个人信息</Heading>
    <VStack spacing={4}>
      <FormControl>
        <FormLabel>姓名</FormLabel>
        <Input />
      </FormControl>
      <FormControl>
        <FormLabel>邮箱</FormLabel>
        <Input type="email" />
      </FormControl>
    </VStack>
  </Box>

  <Divider />

  <Box>
    <Heading size="sm" mb={4}>地址信息</Heading>
    <VStack spacing={4}>
      <FormControl>
        <FormLabel>地址</FormLabel>
        <Input />
      </FormControl>
      <SimpleGrid columns={2} spacing={4} w="full">
        <FormControl>
          <FormLabel>城市</FormLabel>
          <Input />
        </FormControl>
        <FormControl>
          <FormLabel>邮编</FormLabel>
          <Input />
        </FormControl>
      </SimpleGrid>
    </VStack>
  </Box>
</VStack>

FormControl 属性速查表 #

属性 说明
isRequired 必填
isInvalid 无效
isDisabled 禁用
isReadOnly 只读

下一步 #

现在你已经掌握了 Form 表单组件,接下来学习 Table 表格,了解如何展示数据!

最后更新:2026-03-28