在2020年创建React表单

哈Ha 我向您介绍Kristofer Selbekk撰写的文章“在2020年在React中创建表单”翻译

图片


输入栏 文字区域。单选按钮和复选框。以下是我们作为开发人员与用户进行交互的一些要点。我们将它们放置在网站上,用户将其填写,如果幸运的话,完成的表格就不会出现验证错误。

表单处理是大量Web应用程序不可或缺的一部分,这是React最好的事情之一。您拥有创建和处理表格的巨大自由。但是有更好的方法吗?

请注意,在所有这些示例中,我们将创建一个包含电子邮件和密码字段的登录表单,但是这些方法可用于大多数类型的表单。

记住良好形式的规则


尽管这与所讨论的主题没有直接关系,但我想确保您没有忘记使您的表单对所有人都可以理解。在输入字段中添加标签,为输入无效的情况设置正确的aria属性,并在语义上正确地构造内容。这使得用户和开发人员都可以轻松使用表单。

使用状态挂钩处理表单


首先,让我们看看我通常如何使用表单状态。我将所有字段另存为单独的状态元素,并分别更新它们,如下所示:

function LoginForm() {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    api.login(email, password);
  };
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor='email'>Email</label>
        <input type='email' id='email' value={email} onChange={(e) => setEmail(e.target.value)} />
      </div>
      <div>
        <label htmlFor='password'>Password</label>
        <input type='password' id='password' value={password} onChange={(e) => setPassword(e.target.value)} />
      </div>
    </form>
  );
}

首先,我创建状态的两个独立部分-用户名和密码。然后将这两个变量传送到相应的输入字段,以确定该字段的值。每当字段中的某些内容发生更改时,我们一定会更新状态值,从而导致应用程序重绘。

这种表单处理在大多数情况下都可以正常工作,并且简单易用和易于理解。但是,每次编写这样的构造都是很乏味的。

创建一个自定义钩子


让我们做一些重构并创建我们自己的钩子,这将稍微改善我们的代码:

const useFormField = (initialValue: string = '') => {
  const [value, setValue] = React.useState(initialValue);
  const onChange = React.useCallback((e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value), []);
  return { value, onChange };
};

export function LoginForm() {
  const emailField = useFormField();
  const passwordField = useFormField();

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    api.login(emailField.value, passwordField.value);
  };
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor='email'>Email</label>
        <input type='email' id='email' {...emailField} />
      </div>
      <div>
        <label htmlFor='password'>Password</label>
        <input type='password' id='password' {...passwordField} />
      </div>
    </form>
  );
}

我们创建一个自定义useFormField钩子,该钩子为我们创建一个onChange事件处理程序,并将该值存储在表单状态中。在这种情况下,我们可以将钩子与所有表单字段一起使用。

处理大量字段


这种方法的缺点是,随着表单的增长,它无法很好地扩展。对于诸如登录之类的小型表单,这可能很好,但是在创建用户个人资料表单时,您可能需要请求很多信息!我们应该一次又一次地调用钩子吗?

每当遇到类似情况时,我都会编写一个自定义钩子,将所有表单存储在一个大片段中。它可能看起来像这样:

export function LoginForm() {
  const { formFields, createChangeHandler } = useFormFields({
    email: '',
    password: '',
  });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    api.login(formFields.email, formFields.password);
  };
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor='email'>Email</label>
        <input type='email' id='email' value={formFields.email} onChange={createChangeHandler('email')} />
      </div>
      <div>
        <label htmlFor='password'>Password</label>
        <input type='password' id='password' value={formFields.password} onChange={createChangeHandler('password')} />
      </div>
    </form>
  );
}

使用useFormFields挂钩,我们可以继续添加字段,而不会增加组件的复杂性。我们可以在一个地方访问所有表单状态,我们的代码看起来简洁明了。当然,在某些情况下,您可能需要使用setState,但是对于大多数形式,上述技术都很好。

替代方法


因此,状态处理可以正常工作,并且在大多数情况下,这是React中的首选方法。但是您知道还有另一种方法吗?事实证明,浏览器默认情况下会处理表单的内部状态,我们可以使用它来简化代码!

这是相同的表单,但是允许浏览器处理表单的状态:

export function LoginForm() {
  const handleSubmit = (e: React.FormEvent) => {
	e.preventDefault();
	const formData = new FormData(e.target as HTMLFormElement);
	api.login(formData.get('email'), formData.get('password'));
  };
  return (
	<form onSubmit={handleSubmit}>
  	<div>
    	<label htmlFor="email">Email</label>
    	<input
      	type="email"
      	id="email"
      	name="email"
    	/>
  	</div>
  	<div>
    	<label htmlFor="password">Password</label>
    	<input
      	type="password"
      	id="password"
      	name="password"
    	/>
  	</div>
  	<button>Log in</button>
	</form>
  );
}

看看有多容易!没有使用单个钩子,没有初始值设置,没有onChange处理程序。最好的部分是代码仍然可以像以前一样工作-但是如何?

您可能已经注意到,我们在handleSubmit函数中所做的操作有些不同。我们使用称为FormData的内置浏览器API。 FormData是一种从我们的输入字段获取值的便捷(且得到良好支持)的方法!

我们通过Submit事件的target属性获得到DOM中表单的链接,并创建FormData类的新实例。现在,我们可以通过调用formData.get(“输入字段名称”)来通过其名称属性获取所有字段。

这样,您就无需显式处理表单的状态。如果您需要默认值(例如,如果您从数据库或本地存储中更改初始字段值),React为此提供了一个方便的defaultValue选项。

何时使用每种方法


由于表单是大多数Web应用程序不可或缺的一部分,因此了解如何处理它们很重要。 React为您提供了许多方法来做到这一点。
对于不需要严格验证(或可以依赖HTML5表单验证控件)的简单表单,我建议您仅使用DOM默认提供给我们的内置状态处理。有很多事情是您无法做的(例如,以编程方式更改输入值或实时检查),但是在最简单的情况下(例如,如上所述的搜索字段或登录字段),您可能会发现它很适合我们使用浏览器API的替代方法。

当您执行自定义检查或在提交表单之前需要访问某些表单数据时,需要使用受控组件来显式处理状态。您可以使用常规的useStateHooks或创建自定义钩子来简化代码。

值得注意的是,在大多数情况下,React开发人员建议使用受控组件(明确处理组件的状态),因为这种方法将来会给您带来更大的灵活性。在我看来,开发人员常常为了灵活性而牺牲了简单性,而他们通常并不需要这种灵活性。

无论您决定使用哪种方法,React表单处理都从未像现在这样简单。您可以让浏览器处理简单表单,同时在情况需要时显式处理表单状态。希望以上方法对您有所帮助,并通过减少代码中的行数来帮助您解决问题。

All Articles