处理用户的输入

function App() {
  const [username, setUsername] = useState("");
  const [list, setList] = useState([]);

  function handleUsernameOnChange(e) {
    setUsername(e.target.value);
  }
  function handleReset() {
    setUsername("");
  }
  function handlePublish() {
    setList([...list, username]);
    setUsername("");
  }
  return (
    <div className="App">
      <ul>
        {list.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
      <div>
        <label>username</label>
        <input
          type="text"
          onChange={(e) => handleUsernameOnChange(e)}
          value={username}
        />
      </div>
      <button onClick={handlePublish}>publish</button>
      <button onClick={handleReset}>reset</button>
    </div>
  );
}

处理Input、Select、Radio、Checkbox

function App() {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [repeatPassword, setRepeatPassword] = useState("");
  const [gender, setGender] = useState("");
  const [occupation, setOccupation] = useState("");
  const [hobbies, setHobbies] = useState([]);

  function handleHobbiesChange(e) {
    const { value, checked } = e.target;
    if (checked) {
      setHobbies([...hobbies, value]);
    } else {
      setHobbies(hobbies.filter((hobby) => hobby !== value));
    }
  }
  return (
    <div className="App">
      <form>
        <label htmlFor="username">username</label>
        <input
          id="username"
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
        <label htmlFor="password">password</label>
        <input
          id="password"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
        <label htmlFor="repeatPassword">repeat password</label>
        <input
          id="repeatPassword"
          type="password"
          value={repeatPassword}
          onChange={(e) => setRepeatPassword(e.target.value)}
        />
        <label htmlFor="gender">gender</label>
        <fieldset id="gender">
          <input
            type="radio"
            name="gender"
            value="male"
            id="male"
            checked={gender === "male"}
            onChange={(e) => setGender(e.target.value)}
          />
          <label htmlFor="male">man</label>
          <input
            type="radio"
            name="gender"
            value="female"
            id="female"
            checked={gender === "female"}
            onChange={(e) => setGender(e.target.value)}
          />
          <label htmlFor="female">woman</label>
        </fieldset>
        <label htmlFor="occupation">occupation</label>
        <select id="occupation" onChange={(e) => setOccupation(e.target.value)}>
          <option value="student">student</option>
          <option value="teacher">teacher</option>
          <option value="engineer">engineer</option>
        </select>
        <label htmlFor="hobbies">hobbies</label>
        <fieldset id="hobbies">
          <input
            type="checkbox"
            name="hobby"
            id="programming"
            value="programming"
            checked={hobbies.includes("programming")}
            onChange={handleHobbiesChange}
          />
          <label htmlFor="programming">programming</label>
          <input
            type="checkbox"
            name="hobby"
            id="paint"
            value="paint"
            checked={hobbies.includes("paint")}
            onChange={handleHobbiesChange}
          />
          <label htmlFor="paint">paint</label>
          <input
            type="checkbox"
            name="hobby"
            id="music"
            value="music"
            checked={hobbies.includes("music")}
            onChange={handleHobbiesChange}
          />
          <label htmlFor="music">music</label>
        </fieldset>
      </form>

      <ul>
        <li>username: {username}</li>
        <li>password: {password}</li>
        <li>repeat password: {repeatPassword}</li>
        <li>gender: {gender}</li>
        <li>occupation: {occupation}</li>
        <li>hobbies: {hobbies.join(", ")}</li>
      </ul>
    </div>
  );
}

将状态写成对象形式、集成tailwindcss、处理表单提交和重置

function App() {
  const initialUser = {
    username: "",
    password: "",
    repeatPassword: "",
    gender: "",
    occupation: "",
    hobbies: [],
  };

  const [user, setUser] = useState(initialUser);

  function handleFormReset(e) {
    e.preventDefault();
    setUser(initialUser);
  }

  function handleFormSubmit(e) {
    e.preventDefault();
    console.log(user);
  }

  function handleHobbiesChange(e) {
    const { value, checked } = e.target;
    if (checked) {
      setUser((prevUser) => ({
        ...prevUser,
        hobbies: [...prevUser.hobbies, value],
      }));
    } else {
      setUser((prevUser) => ({
        ...prevUser,
        hobbies: prevUser.hobbies.filter((hobby) => hobby !== value),
      }));
    }
  }
  return (
    <div className="container mx-auto h-screen px-4 flex flex-col items-center justify-center gap-y-2.5">
      <form
        className="w-3/5 border-2 p-8"
        onReset={handleFormReset}
        onSubmit={handleFormSubmit}
      >
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="username"
        >
          username
        </label>
        <input
          className="w-96 border-2 border-gray-300 p-2 text-lg outline-none"
          id="username"
          type="text"
          value={user.username}
          onChange={(e) => setUser({ ...user, username: e.target.value })}
        />
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="password"
        >
          password
        </label>
        <input
          className="w-96 border-2 border-gray-300 p-2 text-lg outline-none"
          id="password"
          type="password"
          value={user.password}
          onChange={(e) => setUser({ ...user, password: e.target.value })}
        />
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="repeatPassword"
        >
          repeat password
        </label>
        <input
          className="w-96 border-2 border-gray-300 p-2 text-lg outline-none"
          id="repeatPassword"
          type="password"
          value={user.repeatPassword}
          onChange={(e) => setUser({ ...user, repeatPassword: e.target.value })}
        />
        <label className="block text-lg font-medium tracking-wide text-slate-600">
          gender
        </label>
        <fieldset className="flex space-x-1" id="gender">
          <input
            type="radio"
            name="gender"
            value="male"
            id="male"
            checked={user.gender === "male"}
            onChange={(e) => setUser({ ...user, gender: e.target.value })}
          />
          <label
            className="text-lg font-medium tracking-wide text-slate-600"
            htmlFor="male"
          >
            man
          </label>
          <input
            type="radio"
            name="gender"
            value="female"
            id="female"
            checked={user.gender === "female"}
            onChange={(e) => setUser({ ...user, gender: e.target.value })}
          />
          <label
            className="text-lg font-medium tracking-wide text-slate-600"
            htmlFor="female"
          >
            woman
          </label>
        </fieldset>
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="occupation"
        >
          occupation
        </label>
        <select
          className="border-2 p-1 text-md outline-none"
          id="occupation"
          onChange={(e) => setUser({ ...user, occupation: e.target.value })}
        >
          <option value="student">student</option>
          <option value="teacher">teacher</option>
          <option value="engineer">engineer</option>
        </select>
        <label className="block text-lg font-medium tracking-wide text-slate-600">
          hobbies
        </label>
        <fieldset className="flex gap-1" id="hobbies">
          <input
            type="checkbox"
            name="hobby"
            id="programming"
            value="programming"
            checked={user.hobbies.includes("programming")}
            onChange={handleHobbiesChange}
          />
          <label
            className="leading-4 select-none text-lg font-medium tracking-wide text-slate-600"
            htmlFor="programming"
          >
            programming
          </label>
          <input
            type="checkbox"
            name="hobby"
            id="paint"
            value="paint"
            checked={user.hobbies.includes("paint")}
            onChange={handleHobbiesChange}
          />
          <label
            className="leading-4 select-none text-lg font-medium tracking-wide text-slate-600"
            htmlFor="paint"
          >
            paint
          </label>
          <input
            type="checkbox"
            name="hobby"
            id="music"
            value="music"
            checked={user.hobbies.includes("music")}
            onChange={handleHobbiesChange}
          />
          <label
            className="leading-4 select-none text-lg font-medium tracking-wide text-slate-600"
            htmlFor="music"
          >
            music
          </label>
        </fieldset>
        <button
          className="border-2 rounded-lg px-4 py-2 m-2 bg-blue-400 font-medium text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out"
          type="submit"
        >
          提交
        </button>
        <button
          className="border-2 rounded-lg px-4 py-2 m-2 bg-blue-400 font-medium text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out"
          type="reset"
        >
          重置
        </button>
      </form>
      <ul className="w-3/5 border-2 p-8">
        <li>username: {user.username}</li>
        <li>password: {user.password}</li>
        <li>repeat password: {user.repeatPassword}</li>
        <li>gender: {user.gender}</li>
        <li>occupation: {user.occupation}</li>
        <li>hobbies: {user.hobbies.join(", ")}</li>
      </ul>
    </div>
  );
}

表单校验

import "./App.css";
import { useState } from "react";

function App() {
  const initialUser = {
    username: "",
    password: "",
    repeatPassword: "",
    gender: "",
    occupation: "",
    hobbies: [],
  };

  const [user, setUser] = useState(initialUser);
  const [errors, setErrors] = useState({});

  const rules = {
    username: (value) => {
      if (value.length < 3 || value.length > 10) {
        return "用户名长度应在3-20之间";
      }
    },
    password: (value) => {
      return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/.test(
        value
      )
        ? ""
        : "密码必须包含大小写字母和数字和字符,长度不少于8";
    },
  };

  function handleFormReset(e) {
    e.preventDefault();
    setUser(initialUser);
  }

  function handleFormSubmit(e) {
    e.preventDefault();
    console.log(user);
  }

  function handleHobbiesChange(e) {
    const { value, checked } = e.target;
    if (checked) {
      setUser((prevUser) => ({
        ...prevUser,
        hobbies: [...prevUser.hobbies, value],
      }));
    } else {
      setUser((prevUser) => ({
        ...prevUser,
        hobbies: prevUser.hobbies.filter((hobby) => hobby !== value),
      }));
    }
  }

  function handleUsernameChange(e) {
    const { value, name } = e.target;
    const error = rules[name] && rules[name](value);
    setUser((prevUser) => ({ ...prevUser, [name]: value }));
    setErrors((prevErrors) => ({ ...prevErrors, [name]: error }));
  }

  function handlePasswordChange(e) {
    const { value, name } = e.target;
    const error = rules[name] && rules[name](value);
    setUser((prevUser) => ({ ...prevUser, [name]: value }));
    setErrors((prevErrors) => ({ ...prevErrors, [name]: error }));
  }
  return (
    <div className="container mx-auto h-screen px-4 flex flex-col items-center justify-center gap-y-2.5">
      <form
        className="w-3/5 border-2 p-8"
        onReset={handleFormReset}
        onSubmit={handleFormSubmit}
      >
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="username"
        >
          username
        </label>
        <input
          className="w-96 border-2 border-gray-300 p-2 text-lg outline-none"
          id="username"
          type="text"
          name="username"
          value={user.username}
          onChange={handleUsernameChange}
        />
        {errors.username && (
          <div className="text-red-500">{errors.username}</div>
        )}
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="password"
        >
          password
        </label>
        <input
          className="w-96 border-2 border-gray-300 p-2 text-lg outline-none"
          id="password"
          type="password"
          name="password"
          value={user.password}
          onChange={handlePasswordChange}
        />
        {errors.password && (
          <div className="text-red-500">{errors.password}</div>
        )}
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="repeatPassword"
        >
          repeat password
        </label>
        <input
          className="w-96 border-2 border-gray-300 p-2 text-lg outline-none"
          id="repeatPassword"
          type="password"
          value={user.repeatPassword}
          onChange={(e) => setUser({ ...user, repeatPassword: e.target.value })}
        />
        <label className="block text-lg font-medium tracking-wide text-slate-600">
          gender
        </label>
        <fieldset className="flex space-x-1" id="gender">
          <input
            type="radio"
            name="gender"
            value="male"
            id="male"
            checked={user.gender === "male"}
            onChange={(e) => setUser({ ...user, gender: e.target.value })}
          />
          <label
            className="text-lg font-medium tracking-wide text-slate-600"
            htmlFor="male"
          >
            man
          </label>
          <input
            type="radio"
            name="gender"
            value="female"
            id="female"
            checked={user.gender === "female"}
            onChange={(e) => setUser({ ...user, gender: e.target.value })}
          />
          <label
            className="text-lg font-medium tracking-wide text-slate-600"
            htmlFor="female"
          >
            woman
          </label>
        </fieldset>
        <label
          className="block text-lg font-medium tracking-wide text-slate-600"
          htmlFor="occupation"
        >
          occupation
        </label>
        <select
          className="border-2 p-1 text-md outline-none"
          id="occupation"
          onChange={(e) => setUser({ ...user, occupation: e.target.value })}
        >
          <option value="student">student</option>
          <option value="teacher">teacher</option>
          <option value="engineer">engineer</option>
        </select>
        <label className="block text-lg font-medium tracking-wide text-slate-600">
          hobbies
        </label>
        <fieldset className="flex gap-1" id="hobbies">
          <input
            type="checkbox"
            name="hobby"
            id="programming"
            value="programming"
            checked={user.hobbies.includes("programming")}
            onChange={handleHobbiesChange}
          />
          <label
            className="leading-4 select-none text-lg font-medium tracking-wide text-slate-600"
            htmlFor="programming"
          >
            programming
          </label>
          <input
            type="checkbox"
            name="hobby"
            id="paint"
            value="paint"
            checked={user.hobbies.includes("paint")}
            onChange={handleHobbiesChange}
          />
          <label
            className="leading-4 select-none text-lg font-medium tracking-wide text-slate-600"
            htmlFor="paint"
          >
            paint
          </label>
          <input
            type="checkbox"
            name="hobby"
            id="music"
            value="music"
            checked={user.hobbies.includes("music")}
            onChange={handleHobbiesChange}
          />
          <label
            className="leading-4 select-none text-lg font-medium tracking-wide text-slate-600"
            htmlFor="music"
          >
            music
          </label>
        </fieldset>
        <button
          className="border-2 rounded-lg px-4 py-2 m-2 bg-blue-400 font-medium text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out"
          type="submit"
        >
          提交
        </button>
        <button
          className="border-2 rounded-lg px-4 py-2 m-2 bg-blue-400 font-medium text-white hover:bg-blue-500 transition-colors duration-300 ease-in-out"
          type="reset"
        >
          重置
        </button>
      </form>
      <ul className="w-3/5 border-2 p-8">
        <li>username: {user.username}</li>
        <li>password: {user.password}</li>
        <li>repeat password: {user.repeatPassword}</li>
        <li>gender: {user.gender}</li>
        <li>occupation: {user.occupation}</li>
        <li>hobbies: {user.hobbies.join(", ")}</li>
      </ul>
    </div>
  );
}

export default App;

最后成果