六、使用表单
表单是一个重要的主题,因为它们在我们构建的应用中非常常见。在本章中,我们将学习如何使用 React 控制的组件构建表单,并发现其中涉及大量样板代码。我们将使用一个流行的库来减少样板代码。这也将帮助我们在应用中构建多个表单。
客户端验证对于我们构建的表单的用户体验至关重要,因此我们将深入讨论这个主题。我们还将介绍如何提交表格。
本章将介绍以下主题:
- 了解受控组件
- 用 React 钩子形式减少样板代码
- 实施验证
- 提交表格
在本章结束时,您将学习如何有效地创建具有关键组件的表单。
技术要求
在本章中,我们将使用以下工具:
- Visual Studio 代码:我们将使用它来编辑我们的 React 代码。可从下载并安装 https://code.visualstudio.com/ 。如果您已经安装了它,请确保它至少是 1.52 版。
- Node.js和npm:可从下载 https://nodejs.org/ 。如果已经安装了这些,请确保 Node.js 至少是版本 8.2,
npm
至少是版本 5.2。 - Q 和 A:我们将从第 5 章与 React 路由的路由中完成的 Q 和前端项目开始。这可在 GitHub 的上获得 https://github.com/PacktPublishing/ASP.NET-Core-5-and-React-Second-Edition
chapter-06/start
文件夹中的。
本章中的所有代码片段可在网上找到 https://github.com/PacktPublishing/ASP.NET-Core-5-and-React-Second-Edition 。要从章节还原代码,可以下载源代码存储库,并在相关编辑器中打开相关文件夹。如果代码为前端代码,则可以在终端中输入npm install
来恢复所需的依赖关系。
查看以下视频以查看代码的运行:https://bit.ly/3pherQp 。
了解受控部件
在本节中,我们将学习如何使用所谓的受控组件来实现表单。受控组件的值与 React 中的状态同步。当我们实现第一个受控组件时,这将更有意义。
让我们在 Visual Studio 代码中打开我们的项目,并将应用标题中的搜索框更改为受控组件。遵循以下步骤:
-
打开
Header.tsx
并添加以下导入:cs import { Link, useSearchParams, } from 'react-router-dom';
-
搜索框的默认值是
criteria
路由查询参数。那么,让我们使用 React Router 的useSearchParams
钩子来获得这个:cs export const Header = () => { const [searchParams] = useSearchParams(); const criteria = searchParams.get('criteria') || ''; const handleSearchInputChange = ... }
-
让我们创建一个可以存储搜索值的状态,将其默认为刚才设置的
criteria
变量:cs const searchParams = new URLSearchParams(location.search); const criteria = searchParams.get('criteria') || ''; const [search, setSearch] = React.useState(criteria);
-
现在,让我们从这个
search
状态驱动搜索框值:cs <input type="text" placeholder="Search..." value={search} onChange={handleSearchChange} css={ ... } />
-
通过在 Visual Studio 代码的终端中运行
npm start
命令来启动应用。 -
Try to type something into the search box in the app header.
你会注意到似乎什么都没有发生;有些东西阻止我们输入值。我们刚刚将该值设置为某个 React 状态,因此 React 现在控制搜索框的值。这就是为什么我们似乎不再能够输入它。
我们正在创建第一个受控输入。然而,如果用户不能在受控输入中输入任何内容,那么受控输入就没有多大用处。那么,我们如何使我们的
input
再次可编辑?答案是我们需要监听对input
值所做的更改,并相应地更新状态。React 然后将呈现状态中的新值。 -
We are already listening to changes with the
handleSearchInputChange
function. So, all we need to do is update the state in the following function, replacing the previousconsole.log
statement with the following:cs const handleSearchChange = (e: ChangeEvent<HTMLInputElement>) => { setSearch(e.currentTarget.value); };
现在,如果我们转到 running 应用并在搜索框中输入一些内容,这一次,它将按预期运行,允许我们在其中输入字符。
-
Now, add a
form
element that's wrapped around theinput
element:cs <form> <input type="text" placeholder="Search..." onChange={handleSearchInputChange} value={search} css={ ... } /> </form>
最终,这将允许用户在按下Enter键时调用搜索。
-
将以下
onSubmit
道具添加到表单元素:cs <form onSubmit={handleSubmit}>
-
在
return
语句cs const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); console.log(search); }; return …
上方添加提交处理程序的实现 11. In the running app, type something into the search box in the app header and press Enter. Open the browser's DevTools and look at the console:
图 6.1–受控部件
现在,如果我们在搜索框中输入字符并按回车,我们将在控制台中看到提交的搜索条件。
-
在终端中按Ctrl+C键,提示停止 app 运行时按Y。
总之,受控组件的值由 React 的状态管理。实现一个更新状态的变更处理程序是很重要的;否则,我们的用户将无法与组件交互。
如果我们要实现一个包含多个受控组件的表单,我们必须创建状态和一个更改事件侦听器来更新每个字段的状态。在实现表单时,需要编写大量的样板代码。是否有一个表单库可以用来减少我们必须编写的重复代码的数量?对我们将在下一节中进行介绍。
用 React 钩形式减少样板代码
在部分中,我们将使用一个名为React Hook Form的流行表单库。这个库减少了我们在实现表单时需要编写的代码量。安装 React 钩子表单后,我们将重构在上一节中创建的搜索表单。然后,我们将使用 React-Hook 表单实现用于提问和回答问题的表单。
安装 React 钩模板
让我们通过在终端中输入以下命令来安装 React Hook Form:
> npm install react-hook-form
几秒钟后,将安装 React 钩形。
react-hook-form
包包含 TypeScript 类型,因此这些类型不在我们需要安装的单独包中。
接下来,我们将开始在Header
组件中使用 React Hook 表单作为搜索表单。
重构 Header 组件以使用 React Hook 表单
我们将使用使用 React Hook 表单来减少Header
组件中的代码量。打开Header.tsx
并按照以下步骤操作:
-
Remove the line where the
search
state is declared. The line you must remove is shown here:cs const [search, setSearch] = React.useState(criteria);
React 钩子窗体将管理字段状态,因此我们不必为此编写显式代码。
-
Add the following name property to the
input
element:cs <input name="search" type="text" … />
name
属性是 React Hook 表单所必需的,并且对于给定表单必须具有唯一的值。我们最终将能够使用此名称从input
元素访问此值。 -
Remove the
value
andonChange
properties from theinput
element and add adefaultValue
property, which is set to thecriteria
query parameter value:cs <input name="search" type="text" placeholder="Search..." defaultValue={criteria} css={ ... } />
React 表单钩子最终将管理输入的值。
注意,
defaultValue
是input
元素用于设置其初始值的属性。 -
删除输入的更改事件的
handleSearchInputChange
函数处理程序。 -
从
handleSubmit
函数中删除console.log
语句:cs const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); };
-
现在我们已经删除了样板表单代码,现在是使用 React 钩子表单的时候了。让我们从 React hook 表单
cs import { useForm } from 'react-hook-form';
导入
useForm
钩子开始 7. 在Header
组件定义上方和import
语句下方添加一个表示表单数据的类型:cs type FormData = { search: string; };
-
将表单数据类型传递到
useForm
钩子中,并从中解构函数:cs export const Header = () => { const { register } = useForm<FormData>(); const [searchParams] = useSearchParams(); ...
-
Add a
ref
property to theinput
element in theHeader
component's JSX and set this to theregister
function from React Hook Form:cs <input ref={register} name="search" type="text" placeholder="Search..." defaultValue={criteria} css={ ... } />
register
函数允许input
元素注册到 React Hook 表单,然后由其管理。需要将其设置为元素的ref
属性。重要提示
ref
属性是一个特殊的属性,它添加到允许访问底层 DOM 节点的元素中。
我们表格的代码现在短了很多。这是因为 React 钩子表单保存字段状态并管理对其的更新。
我们使用useForm
钩子中的register
函数告诉 React 钩子表单要管理哪些字段。useForm
钩子中还有其他有用的函数和对象,我们将在本章中学习和使用。
React Form Hook 现在控制搜索输入字段。我们将回到这里,在提交表单部分实现搜索提交功能。
下一步,我们将把注意力转向形式的设计。
创建表单样式的组件
在本节中,我们将创建一些样式的组件,这些组件可以在我们最终实现的表单中使用。打开Styles.ts
并执行以下步骤:
-
从 Emotion 导入
css
功能:cs import { css } from '@emotion/react';
-
Add a styled component for a
fieldset
element:cs export const Fieldset = styled.fieldset` margin: 10px auto 0 auto; padding: 30px; width: 350px; background-color: ${gray6}; border-radius: 4px; border: 1px solid ${gray5}; box-shadow: 0 3px 5px 0 rgba(0, 0, 0, 0.16); `;
我们最终会在表单中使用
fieldset
元素。 -
为字段容器添加样式化组件:
cs export const FieldContainer = styled.div` margin-bottom: 10px; `;
-
为元素
cs export const FieldLabel = styled.label` font-weight: bold; `;
添加一个样式化的组件 5. 字段编辑器元素将具有许多常见的 CSS 属性。创建包含以下代码的变量:
cs const baseFieldCSS = css` box-sizing: border-box; font-family: ${fontFamily}; font-size: ${fontSize}; margin-bottom: 5px; padding: 8px 10px; border: 1px solid ${gray5}; border-radius: 3px; color: ${gray2}; background-color: white; width: 100%; :focus { outline-color: ${gray5}; } :disabled { background-color: ${gray6}; } `;
-
Use the following variable in a styled component for an
input
element:cs export const FieldInput = styled.input` ${baseFieldCSS} `;
这导致输入元素在我们正在创建的新样式组件中包含来自
baseFieldCSS
变量的 CSS。 -
现在,为
textarea
元素cs export const FieldTextArea = styled.textarea` ${baseFieldCSS} height: 100px; `;
创建一个样式化组件 8. 为验证错误消息添加样式化组件:
cs export const FieldError = styled.div` font-size: 12px; color: red; `;
-
Add a styled component for a container for the form submit button:
cs export const FormButtonContainer = styled.div` margin: 30px 0px 0px 0px; padding: 20px 0px 0px 0px; border-top: 1px solid ${gray5}; `;
我们将要创建的最后一个样式化的组件是提交消息:
cs export const SubmissionSuccess = styled.div` margin-top: 10px; color: green; `; export const SubmissionFailure = styled.div` margin-top: 10px; color: red; `;
有了这些,我们已经实现了我们将在表单中使用的所有样式化组件。
现在我们已经实现了这些样式化组件,我们将使用它们来实现下一个表单。
实施 ask 表格
现在,是时候实施表单了,这样我们的用户就可以提问了。我们将通过利用 React 钩子表单和表单的样式化组件来实现这一点。遵循以下步骤:
-
打开
AskPage.tsx
并添加以下import
语句:cs import { Fieldset, FieldContainer, FieldLabel, FieldInput, FieldTextArea, FormButtonContainer, PrimaryButton, } from './Styles'; import { useForm } from 'react-hook-form';
-
添加一个类型来表示
AskPage
组件定义正上方和import
语句正下方的表单数据:cs type FormData = { title: string; content: string; };
-
向
AskPage
组件添加显式的return
语句,将FormData
类型传递给useForm
钩子,并对寄存器函数进行解构 -
从中:
cs export const AskPage = () => { const { register } = useForm<FormData>(); return ( <Page title="Ask a question"> {null} </Page> ); }
-
增加的
form
和Fieldset
元素,替换的null
输出:cs <Page title="Ask a question"> <form> <Fieldset> </Fieldset> </form> </Page>
-
Now, let's add a field that will capture the question's title:
cs <Fieldset> <FieldContainer> <FieldLabel htmlFor="title"> Title </FieldLabel> <FieldInput id="title" name="title" type="text" ref={register} /> </FieldContainer> </Fieldset>
注意如何使用
htmlFor
属性将标签绑定到输入。此表示当输入具有焦点时,屏幕阅读器将读取标签。此外,单击标签将自动设置输入的焦点。 -
添加将捕获问题内容的字段:
cs <Fieldset> <FieldContainer> ... </FieldContainer> <FieldContainer> <FieldLabel htmlFor="content"> Content </FieldLabel> <FieldTextArea id="content" name="content" ref={register} /> </FieldContainer> </Fieldset>
-
最后,添加一个按钮,用于使用
FormButtonContainer
和PrimaryButton
样式的组件提交问题:cs <Fieldset> <FieldContainer> ... </FieldContainer> <FormButtonContainer> <PrimaryButton type="submit"> Submit Your Question </PrimaryButton> </FormButtonContainer> </Fieldset>
-
通过在 Visual Studio 代码的终端中运行
npm start
命令来启动应用。 - 让我们在 running 应用中尝试一下,点击主页上的提问按钮:
图 6.2–提问表格
我们的表单按预期呈现。
React-Hook 表单和样式化表单组件使这项工作变得非常简单。现在
让我们尝试实现另一个表单,它正是答案表单。
实施答疑表
让我们在问题页面上实现答案表单。遵循以下步骤:
-
打开
QuestionPage.tsx
并更新以下import
语句:cs import { gray3, gray6, Fieldset, FieldContainer, FieldLabel, FieldTextArea, FormButtonContainer, PrimaryButton, } from './Styles';
-
为 React Hook 表单
cs import { useForm } from 'react-hook-form';
添加导入语句 3. 添加表示表单数据的类型:
cs type FormData = { content: string; };
-
将表单数据类型传递给
useForm
钩子,并从中解构register
函数:cs export const QuestionPage = () => { ... const { register } = useForm<FormData>(); return ... }
-
Let's create our form in the JSX, just beneath the list of answers:
``cs <AnswerList data={question.answers} /> <form css={css
margin-top: 20px; `}Your Answer Submit Your Answer ```
因此,表单将包含一个字段作为答案内容,提交按钮将有标题提交您的答案。
-
让我们在 running 应用中尝试一下,单击主页上的一个问题:
图 6.3–答案表
我们的表单按预期呈现。
我们现在已经用 React-Hook 表单构建了三个表单,并亲身体验了它如何简化构建字段。在此过程中,我们还构建了一组方便的样式化表单组件。
我们的表格看起来不错,但还没有验证。例如,我们可以对一个问题提交一个空白答案,但由于目前还没有实现这样的机制,因此无法对其进行验证。在下一节中,我们将通过验证来增强表单。
实施验证
在表单上包含验证可以改善用户体验,因为您可以立即反馈输入的信息是否有效。在本节中,我们将向表单中添加用于提问和回答问题的验证规则。这些验证规则将包括检查,以确保字段已填充且包含一定数量的字符。
在 ask 表单上实施验证
我们将通过以下步骤对 ask 表进行验证:
-
在
AskPage.tsx
中,我们将确保用户使用最少的字符数填充标题和内容字段。首先,导入FieldError
样式的组件:cs import { ... FieldError, } from './Styles';
-
分解来自
useForm
钩子的表单错误消息:cs const { register, errors } = useForm<FormData>();
-
Next, configure the form so that it invokes the validation rules when the field elements lose focus (that is, the element's
blur
event):cs const { register, errors } = useForm<FormData>({ mode: 'onBlur', });
请务必注意,提交表单时也会验证字段。
-
Specify the validation rules in the register function, as follows:
cs <FieldInput id="title" name="title" type="text" ref={register({ required: true, minLength: 10, })} />
突出显示的代码表明,
title
字段是必需的,必须至少有 10 个字符长。 -
Let's conditionally render the validation error messages for both these rules beneath the
input
element:cs <FieldInput ... /> {errors.title && errors.title.type === 'required' && ( <FieldError> You must enter the question title </FieldError> )} {errors.title && errors.title.type === 'minLength' && ( <FieldError> The title must be at least 10 characters </FieldError> )} </FieldContainer>
errors
是钩形为我们维护的对象。对象中的键对应于FieldInput
中的 name 属性。每个错误中的type
属性指定错误用于哪个规则。 -
将验证添加到
content
字段,使其成为必填项。至少包含 50 个字符:cs <FieldTextArea id="content" name="content" ref={register({ required: true, minLength: 50, })} />
-
Add a validation error message to the
content
field:cs <FieldTextArea ... /> {errors.content && errors.content.type === 'required' && ( <FieldError> You must enter the question content </FieldError> )} {errors.content && errors.content.type === 'minLength' && ( <FieldError> The content must be at least 50 characters </FieldError> )} </FieldContainer>
当内容字段未通过验证检查时,
errors
对象将包含一个content
属性。content
属性中的 type 属性表示违反了哪条规则。因此,我们在errors
对象中使用此信息来呈现适当的验证消息。 -
Let's give this a try. In the running app, go to the ask page by clicking on the Ask a question button at the bottom of the home screen.
不在表单中输入任何内容,单击进入和退出字段。您将看到,表单呈现了验证错误,这意味着我们实现的机制已经成功运行。不要在标题字段中键入任何内容,然后输入少于 50 个字符的内容:
图 6.4–ask 表格上的验证
在这里,我们可以看到,验证错误在我们从字段中移出时呈现。
对答题表进行验证
让我们对答案表进行验证。我们将验证内容是否已填入至少 50 个字符。要执行此操作,请执行以下步骤:
-
打开
QuestionPage.tsx
导入FieldError
样式组件:cs import { ... FieldError, } from './Styles';
-
在
useForm
钩子中,对errors
对象进行解构,并将表单配置为在其字段失去焦点时进行验证:cs const { register, errors } = useForm<FormData>({ mode: 'onBlur', });
-
Specify the validation rules on the answer field, as follows:
cs <FieldTextArea id="content" name="content" ref={register({ required: true, minLength: 50, })} />
在这里,我们已经指定答案必须是强制性的,并且长度必须至少为 50 个字符。
-
让我们有条件地在文本区域元素
cs <FieldTextArea ... /> {errors.content && errors.content.type === 'required' && ( <FieldError> You must enter the answer </FieldError> )} {errors.content && errors.content.type === 'minLength' && ( <FieldError> The answer must be at least 50 characters </FieldError> )} </FieldContainer>
下呈现这两个规则的验证错误消息 5. 在 running app 中,我们可以通过点击主页上的一个问题并输入
Some answer
来检查这是否按预期工作:
图 6.5–答案表上的验证
这样,我们就完成了表单验证的实现。React Hook 表单有一组有用的验证规则,可以将应用于其register
函数。React Hook 表单中的errors
对象为我们提供了输出信息性验证错误消息所需的所有信息。更多关于 React Hook 表单验证的信息,请访问https://react-hook-form.com/get-started#Applyvalidation 。
我们的最终任务是在用户提交表单时执行提交逻辑。我们将在下一节中进行此操作。
提交表格
提交表格是表格执行的最后一部分。我们将在所有三个表单中实现表单提交逻辑,从搜索表单开始。
提交逻辑是使用表单中的数据执行任务的逻辑。通常,此任务将涉及将数据发布到 web API 以执行服务器端任务,例如将数据保存到数据库表。在本节中,我们的提交逻辑将简单地调用模拟 web API 调用的函数。
在搜索表单中实现表单提交
在Header.tsx
中,执行以下步骤在搜索表单上实现表单提交:
-
从 React 路由导入
useNavigate
钩子:cs import { Link, useSearchParams, useNavigate, } from 'react-router-dom';
-
在
Header
组件的第一行,为useNavigate
钩子的结果分配一个函数:cs const navigate = useNavigate();
-
Destructure a
handleSubmit
function from theuseForm
hook:cs const { register, handleSubmit } = useForm<FormData>();
这与现有的
handleSubmit
冲突,我们将在步骤 5中解决。 -
Use the
handleSubmit
function to handle thesubmit
event in the form:cs <form onSubmit={handleSubmit(submitForm)}>
React Hook 表单的
handleSubmit
函数包含样板代码,例如停止浏览器将表单发布到服务器。请注意,我们已经将
submitForm
传递到handleSubmit
。这是一个包含提交逻辑的函数,我们将在下一步实现它。 -
Overwrite the existing
handleSubmit
function with the followingsubmitForm
function:cs const submitForm = ({ search }: FormData) => { navigate(`search?criteria=${search}`); };
React 钩子表单将表单数据传递给函数。我们从表单数据中解构
search
字段值。提交逻辑以编程方式导航到搜索页面,将
criteria
查询参数设置为search
字段值。 -
让我们在 running 应用中尝试一下。在搜索框中,输入单词
typescript
并按输入,如下所示:
图 6.6-搜索提交
浏览器位置查询参数按预期设置,搜索表单呈现结果正确。
这是在我们的第一个表单中实现的提交。现在,我们将继续以其他形式实现提交逻辑。
在 ask 表单中实现表单提交
让我们执行以下步骤在 ask 表单中实现提交:
-
In
QuestionsData.ts
, create a function that will simulate posting a question:cs export interface PostQuestionData { title: string; content: string; userName: string; created: Date; } export const postQuestion = async ( question: PostQuestionData, ): Promise<QuestionData | undefined> => { await wait(500); const questionId = Math.max(...questions.map(q => q.questionId)) + 1; const newQuestion: QuestionData = { ...question, questionId, answers: [], }; questions.push(newQuestion); return newQuestion; };
此函数使用
Math.max
方法将问题添加到questions
数组中,以将questionId
设置为下一个数字。 -
In
AskPage.tsx
, import the function we just added toQuestionData.ts
:cs import { postQuestion } from './QuestionsData';
另外,导入
SubmissionSuccess
消息样式组件:cs import { ..., SubmissionSuccess, } from './Styles';
-
为在
AskPage
组件cs const [ successfullySubmitted, setSuccessfullySubmitted, ] = React.useState(false);
中是否已成功提交表单添加一些状态 4. 从
useForm
钩子cs const { register, errors, handleSubmit, } = useForm<FormData>({ mode: 'onBlur', });
解构
handleSubmit
函数 5. Also, destructureformState
from theuseForm
hook:cs const { register, errors, handleSubmit, formState, } = useForm<FormData>({ mode: 'onBlur', });
formState
包含表单是否提交、表单是否有效等信息。 -
使用
handleSubmit
功能处理submit
事件,格式为:cs <form onSubmit={handleSubmit(submitForm)}>
-
Create a
submitForm
function just above the component'sreturn
statement, as follows:cs const submitForm = async (data: FormData) => { const result = await postQuestion({ title: data.title, content: data.content, userName: 'Fred', created: new Date() }); setSuccessfullySubmitted(result ? true : false); };
前面的代码异步调用
postQuestion
函数,使用硬编码的用户名和创建日期从表单数据传入标题和内容。 -
Disable the form if the submission is in progress or successfully completed:
```cs <Fieldset disabled={ formState.isSubmitting || successfullySubmitted }
```
isSubmitting
是formState
中的一个标志,指示是否正在进行表单提交。您可以注意到
formState
内有isSubmitted
标志。此表示表格是否已提交且为true
,即使表格无效。这就是为什么我们使用自己的状态(successfullySubmitted
)来表示已提交有效的表格。 -
After
FormButtonContainer
in the JSX, add the following submission success message:cs {successfullySubmitted && ( <SubmissionSuccess> Your question was successfully submitted </SubmissionSuccess> )}
表单成功提交后,将呈现此消息。
-
在跑步应用中,在首页点击提问按钮并填写问题表。然后点击提交问题按钮:
图 6.7–提交问题
表单在成功提交期间和之后被禁用,我们收到预期成功消息。
接下来,我们将在应答表单中实现表单提交。
在应答表中执行表单提交
在应答表中执行以下步骤进行表单提交:
-
In
QuestionsData.ts
, create a function that simulates posting an answer:cs export interface PostAnswerData { questionId: number; content: string; userName: string; created: Date; } export const postAnswer = async ( answer: PostAnswerData, ): Promise<AnswerData | undefined> => { await wait(500); const question = questions.filter( q => q.questionId === answer.questionId, )[0]; const answerInQuestion: AnswerData = { answerId: 99, ...answer, }; question.answers.push(answerInQuestion); return answerInQuestion; };
函数在
questions
数组中找到问题并添加答案。前面代码的其余部分包含 post 答案和函数结果的简单类型。 -
在
QuestionPage.tsx
中,导入我们刚刚创建的函数,同时导入Styles
组件以获得成功消息:cs import { …, postAnswer } from './QuestionsData'; import { …, SubmissionSuccess, } from './Styles';
-
在
QuestionPage
组件cs const [ successfullySubmitted, setSuccessfullySubmitted, ] = React.useState(false);
中增加表单是否已成功提交的状态 4. 从
useForm
吊钩cs const { register, errors, handleSubmit, formState } = useForm<FormData>({ mode: 'onBlur', });
上拆下
handleSubmit
和formState
5. 使用handleSubmit
功能处理submit
事件,格式为:```cs <form onSubmit={handleSubmit(submitForm)} css={…}
```
-
Create a
submitForm
function just above the component'sreturn
statement, as follows:cs const submitForm = async (data: FormData) => { const result = await postAnswer({ questionId: question!.questionId, content: data.content, userName: 'Fred', created: new Date(), }); setSuccessfullySubmitted( result ? true : false, ); };
因此,这将调用
postAnswer
函数,使用硬编码用户名和创建日期从字段值异步传递内容。在引用
question
状态变量后注意!
。这是一个非空断言运算符。重要提示
非空的断言运算符(
!
)告诉 TypeScript 编译器前面的变量不能是null
或undefined
。这在 TypeScript 编译器不够聪明,无法自行解决这一问题的情况下非常有用。因此,
question!.questionId
中的!
阻止 TypeScript 抱怨question
可能是null
。 -
如果提交正在进行或成功完成,请禁用表单:
```cs <Fieldset disabled={ formState.isSubmitting || successfullySubmitted }
```
-
在 JSX 中
FormButtonContainer
之后,添加以下提交成功消息:cs {successfullySubmitted && ( <SubmissionSuccess> Your answer was successfully submitted </SubmissionSuccess> )}
-
在 running 应用的主页上,单击问题。填写答案并提交:
图 6.8–答案提交
与 ask 表单一样,answer 表单在提交期间和提交之后被禁用,我们将收到预期的成功消息。
这是我们的三个表格,很完整,运行良好。
总结
在本章中,我们了解到可以使用 React 中的受控组件实现表单。对于受控组件,React 通过状态控制字段组件值,我们需要实现样板代码来管理此状态。
React Hook 表单是 React 社区中流行的表单库。这就消除了对样板代码的需要,而这正是受控组件所需要的。
我们现在了解到,register
函数可以设置为 React 元素的ref
属性,以允许 React 钩子表单管理该元素。验证规则可以在register
函数参数中指定。
我们可以将表单提交逻辑从 React Hook 表单传递到handleSubmit
函数中。我们了解到,isSubmitting
是formState
中的一个有用标志,我们可以使用它在提交时禁用表单。
在下一章中,我们将重点关注应用中的状态管理并利用 Redux。
问题
通过回答以下问题,检查是否所有关于表单的信息都被卡住了:
- 非受控
input
元素的哪些属性可用于设置其初始值? -
The following JSX is a controlled
input
element:cs <input id="firstName" value={firstName} />
但是,用户无法在输入中输入字符。这里有什么问题?
-
当我们实现如下表单字段时,为什么要使用
htmlFor
属性将label
与input
绑定?cs <label htmlFor="title">{label}</label> <input id="title" … />
-
React Hook 表单中的哪个对象允许我们访问验证错误?
- 我们可以从 React Hook 表单中使用什么来确定表单是否正在提交?
- 我们为什么不使用带有
formState
的isSubmitted
标志来确定表单是否已成功提交?
答案
input
元素的defaultValue
属性可用于设置其初始值。-
问题是对
firstName
状态所做的更改需要管理:cs <input id="firstName" value={firstName} onChange={e => setFirstName(e.currentTarget.value)} />
-
当我们使用
htmlFor
属性将label
绑定到input
时,屏幕阅读器等工具就可以访问它。此外,当用户单击标签时,焦点将自动设置在输入上。 - React Hook 表单中的
errors
对象允许我们访问验证错误。 - 我们可以使用
formState
中的isSubmitting
标志来确定是否正在提交表单。 - 带有
formState
的isSubmitted
标志表示是否已提交表单,无论提交是否成功。
进一步阅读
以下是一些有用的链接,您可以了解有关本章所涵盖主题的更多信息:
- React 形式:https://reactjs.org/docs/forms.html
- React 钩形式:https://react-hook-form.com/
版权属于:月萌API www.moonapi.com,转载请注明出处