向简单的 React + Typescript 应用程序添加验证

Adding validation to simple React + Typescript application

我有一个简单的问题,但我被卡住了,因为我不太了解 TypeScript(我需要很快了解它)。

我必须在提交时添加简单的验证,这将检查值是否不为空。

我有最简单的 React 表单:

type FormValues = {
  title: string;
};

function App() {
  const [values, handleChange] = useFormState<FormValues>({
    title: ""
  });

  const handleSubmit = (evt: FormEvent) => {
    evt.preventDefault();
    console.log("SUBMITTED");
  };

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          id="title"
          name="title"
          onChange={handleChange}
          type="text"
          value={values.title}
        />
        <button type="submit">submit</button>
      </form>
    </div>
  );
}

处理表单的自定义挂钩:

import { useCallback, useReducer } from "react";

type InputChangeEvent = {
  target: {
    name: string;
    value: string;
  };
};

function useFormState<T extends Record<string, string>>(initialValue: T) {
  const [state, dispatch] = useReducer(
    (prevState: T, { name, value }: InputChangeEvent["target"]) => ({
      ...prevState,
      [name]: value
    }),
    initialValue
  );

  const handleDispatch = useCallback(
    (evt: InputChangeEvent) => {
      dispatch({
        name: evt.target.name,
        value: evt.target.value
      });
    },
    [dispatch]
  );

  return [state, handleDispatch] as const;
}

export default useFormState;

现在我想在提交时添加简单的验证并且(因为 TS)我不知道如何做。我考虑了三个选项:

  1. 将验证逻辑放入 handleSubmit 方法。
  2. 将验证逻辑放入自定义 useFormState 挂钩中。
  3. 创建另一个自定义挂钩,仅用于管理验证。

我尝试处理此 CodeSandbox example 中的第一个(我认为是最差的)解决方案,但正如我所说,TS 类型比我强,代码不起作用。

有没有人能帮我解决这两种情况(选择最正确的解决方案,然后,运行 带 TS 的代码)?

一种常见的方法是将表单错误值存储在 React useState 变量中并根据需要呈现它。

const [error, setError] = React.useState('');

// ...

const handleSubmit = (evt: FormEvent) => {
  evt.preventDefault();

  // This might be the wrong way to check the input value
  // I haven't worked directly with form submit events in a hot minute
  if (!evt.target.value) {
    setError('Title is required');
  }
};

// ...

<input
  id="title"
  name="title"
  onChange={handleChange}
  type="text"
  value={values.title}
  aria-describedby="title-error"
  required
  aria-invalid={!!error}
/>
{error && <strong id="title-error" role="alert">{error}</strong>}

注意 aria-describedbyrequiredaria-invalidrole 属性对于加强输入及其错误之间的语义关系很重要,向屏幕宣布错误readers 当它出现时,并将输入指定为“必需”。

如果您在表单中有多个输入,您可以将 error 值放入一个对象中,该对象可以为表单的每个字段存储一个单独的错误:

const [errors, setErrors] = React.useState({});

// ...

setErrors(oldErrors => ({...oldErrors, title: "Title is required"}));

// ...


{errors.title && <strong id="title-error" role="alert">{errors.title}</strong>}

另一种常见模式是在修改或“修改”表单时清除输入错误以允许重新提交表单:

onChange={(e) => {
  setError(''); // or setErrors(({title: _, ...restErrors}) => restErrors);

  handleChange(e);
}}

请注意,所有这些错误处理逻辑 都可以 集成到您的自定义挂钩中以进行一般性的 form/input 处理,但并非必须如此。