redux-form
is a great way of managing forms that are powered by Redux. It is a Higher-Order-Component (HOC) that uses react-redux to make sure HTML forms in React use Redux to store all of its state. In this tutorial, we'll see how to get started with redux-form by building a simple form with validation features.
redux-form
是管理由Redux支持redux-form
的一种好方法。 它是一个使用React -redux来确保React中HTML表单使用Redux来存储其所有状态的高阶组件(HOC)。 在本教程中,我们将了解如何通过构建具有验证功能的简单表单来开始使用redux-form 。
Before getting started on using redux-form
, let's go over a basic overview of redux-form
and it's API.
在开始使用redux-form
,让我们看一下redux-form
及其API的基本概述。
Redux表单概述 ( Overview of redux-form )
formReducer This is a function that tells how to update the Redux store based on changes coming from the application; those changes are described by Redux actions. formReducer
has to be mounted on the Redux state at form
.
formReducer这是一个函数,该函数指示如何根据应用程序中的更改来更新Redux存储。 这些更改由Redux操作描述。 formReducer
必须在form
处安装在Redux状态。
reduxForm() The reduxForm()
function is a higher-order component takes a configuration object and it always returns a new function. It is used to wrap the form component and bind user interaction to the Redux dispatch actions.
reduxForm() reduxForm()
函数是一个带配置对象的高阶组件,它总是返回一个新函数。 它用于包装表单组件,并将用户交互绑定到Redux调度操作。
Field The <Field/>
component is a component that lives inside your wrapped form component. It serves as a way to connect the input elements in a form to the redux-form logic
. Simply put, it's the way we get the input from what users type.
字段 <Field/>
组件是一个位于包装表单组件中的组件。 它用作将表单中的输入元素连接到redux-form logic
。 简而言之,这就是我们从用户键入的内容中获取输入的方法。
You can read more about redux-form
API at the documentation page.
您可以在文档页面上阅读有关redux-form
API的更多信息。
Let's build the form now.
现在建立表格。
用redux-form建立一个表单 ( Build a form with redux-form )
We'll be building a React app with the create-react-app package. create-react-app
allows you to create React apps with no build configuration. You can use create-react-app
by running the terminal command below. It automatically creates a React app for you in a folder titled contact-redux
.
我们将使用create-react-app包构建一个React应用。 create-react-app
允许您创建没有构建配置的React应用。 您可以通过运行以下终端命令来使用create-react-app
。 它会自动在名为contact-redux
的文件夹中为您创建一个React应用。
npx create-react-app contact-redux
If you're wondering what npx
is, it's a new tool that's intended to help with installations of packages from the npm repository. It's a package runner that makes it super easy to install and manage dependencies hosted on the registry. It's important to note that npx
only works with versions of npm
that are 5.2 and above. If you have a version lower than that and would still like to use create-react-app
on your computer. Run the terminal commands below to install create-react-app
and start a React app.
如果您想知道npx
是什么,它是一个新工具,旨在帮助您安装npm存储库中的软件包。 它是一个包运行器,可以非常轻松地安装和管理注册表中托管的依赖项。 请务必注意, npx
仅适用于5.2及更高版本的npm
版本。 如果您的版本低于该版本,并且仍想在计算机上使用create-react-app
。 运行以下终端命令以安装create-react-app
并启动React应用。
npm install -g create-react-app
create-react-app contact-redux
Once the React app has been setup, you can navigate to the directory and start the dev server to see if everything works. Run the npm start
command to start the newly created React app in development mode.
一旦安装了React应用程序,您就可以导航到目录并启动开发服务器以查看一切是否正常。 运行npm start
命令以开发模式启动新创建的React应用。
![](https://scotch-res.cloudinary.com/image/upload/w_auto,q_auto:good,f_auto/media/53535/MAw3oVU6Q6eDXCxMqo7n_Screen%20Shot%202018-02-22%20at%202.41.23%20AM.png)
We now have a React app up and running so let's begin to add the dependencies we'll need for the form.
现在我们已经启动并运行了一个React应用程序,因此让我们开始添加表单所需的依赖项。
npm i redux react-redux redux-form
- redux - A state container and it's a prerequisite for redux-form to work. redux-状态容器,它是redux-form起作用的先决条件。
- react-redux - React Redux is the official React bindings for Redux and it's also a prerequisite for redux-form to work react-redux -React Redux是Redux的官方React绑定,它也是redux-form起作用的前提
- redux-form - The package in use for this tutorial. redux-form-本教程使用的软件包。
Once that's been installed, we can then begin to work on the contact form. We'll add a Bulma CDN link to the index.html
file so as to add some default styling. Open the public/index.html
file and add the following line of code to the head
tag.
安装完成后,我们就可以开始使用联系表了。 我们将Bulma CDN链接添加到index.html
文件,以便添加一些默认样式。 打开public/index.html
文件,并将以下代码行添加到head
标签。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
We'll be making some edits to the src/App.js
file now. Open up the src/App.js
file and add the line of code below at the top of the file.
现在,我们将对src/App.js
文件进行一些编辑。 打开src/App.js
文件,并在文件顶部下方添加以下代码行。
import { reduxForm, Field } from 'redux-form';
创建表格 (Creating the Form)
Next, go to the render()
function, edit it with the following.
接下来,转到render()
函数,使用以下内容对其进行编辑。
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React x redux-form</h1>
</header>
<div className="container">
<p className="App-intro">
Contact Form
</p>
<SignInForm />
</div>
</div>
);
}
The introductory text has been changed and most importantly we added a <SignInForm />
component which we'll create below. It's going to be a simple component that returns the form we need and it will be hooked up to redux-form
component. In the same src/App.js
file, type out this code below just before the declaration of class App extends Component
.
介绍性文本已更改,最重要的是,我们添加了一个<SignInForm />
组件,该组件将在下面创建。 这将是一个简单的组件,它返回我们所需的表单,并将其连接到redux-form
组件。 在同一src/App.js
文件中,在class App extends Component
的声明class App extends Component
之前,在下面键入此代码。
let SignInForm = props => {
return <form className="form">
<div className="field">
<div className="control">
<label className="label">First Name</label>
<Field className="input" name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
<div className="field">
<div className="control">
<label className="label">Last Name</label>
<Field className="input" name="lastName" component="input" type="text" placeholder="Last Name"/>
</div>
</div>
<div className="field">
<div className="control">
<label className="label">Email</label>
<Field className="input" name="email" component="input" type="email" placeholder="Email Address"/>
</div>
</div>
<div className="field">
<div className="control">
<label className="label">Proficiency</label>
<div className="select">
<Field className="input" name="proficiency" component="select">
<option />
<option value="beginner">Beginner Dev</option>
<option value="intermediate">Intermediate Dev</option>
<option value="expert">Expert Dev</option>
</Field>
</div>
</div>
</div>
<div className="field">
<div className="control">
<label className="label">Age</label>
<Field className="input" name="age" component="input" type="number" placeholder="Age"/>
</div>
</div>
<div className="field">
<div className="control">
<label className="label">Gender</label>
<label className="radio">
<Field name="gender" component="input" type="radio" value="male" />
{' '}
Male
</label>
<label className="radio">
<Field name="gender" component="input" type="radio" value="female" />
{' '}
Female
</label>
</div>
</div>
<div className="field">
<div className="control">
<label className="checkbox">
<Field name="saveDetails" id="saveDetails" component="input" type="checkbox"/>
Save Details
</label>
</div>
</div>
<div className="field">
<div className="control">
<label className="label">Message</label>
<Field className="textarea" name="message" component="textarea" />
</div>
</div>
<div className="field">
<div className="control">
<button className="button is-link">Submit</button>
</div>
</div>
</form>;
};
In the code block above, we set up a basic contact form, it asks the user for information such as First Name, Last Name, Age, Gender, e.t.c. The interesting bit in this form is the Field
component. The Field
component is gotten from the redux-form
package and it's how we write the input
field. The type
prop indicates what type of input it should be, that is, a radio
input, a checkbox
input, a text
input or an email
input. The component
prop determines what type of input field it should be, it could be input
, textarea
or select
tags and the name
prop is what will be used to identify the state of the fields in the redux store which we'll create below.
在上面的代码块中,我们设置了一个基本的联系表单,该表单要求用户提供诸如名,姓,年龄,性别等信息,该表单中有趣的一点是Field
组件。 Field
组件是从redux-form
包中获得的,这就是我们编写input
字段的方式。 type
prop指示输入应为哪种类型,即radio
输入, checkbox
输入, text
输入或email
输入。 prop component
确定应该是什么类型的输入字段,可以是input
, textarea
或select
标签,而name
prop就是用来标识我们将在下面创建的redux存储中的字段状态。
So in order to use the form hooked up to redux-form, we need to have some sort of Redux store created already and that's what we're going to do next.
因此,为了使用连接到redux-form的表单,我们需要已经创建了某种Redux存储,这就是我们下一步要做的。
设置Redux存储 (Setup a Redux Store)
We need a redux store in which we can connect the form component (SignInForm
) we created. Let's start by importing the redux
package. Open the src/index.js
file and add the following lines of code in which we are basically importing redux
to the React app.
我们需要一个redux存储,可以在其中存储我们创建的表单组件( SignInForm
)。 让我们从导入redux
包开始。 打开src/index.js
文件,并添加以下代码行,其中我们基本上将redux
导入到React应用程序中。
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { reducer as formReducer } from 'redux-form';
The first line of code imports createStore
and combineReducers
. createStore
helps to create a Redux store that holds the complete state tree of your app and the combineReducers
helps to manage all of your reducer functions into one single helper function which can then be passed into createStore
. You can read more about these function on the Redux API reference page.
第一行代码导入createStore
和combineReducers
。 createStore
帮助创建一个Redux存储,该存储包含应用程序的完整状态树,而combineReducers
帮助将所有reducer功能管理到一个单一的helper函数中,然后可以将其传递到createStore
。 您可以在Redux API 参考页面上阅读有关这些功能的更多信息。
The second line of code imports Provider
from react-redux
. Provider
helps to pass the state of the store to all container components in the app and we'll demonstrate how that works later.
第二行代码从react-redux
导入Provider
。 Provider
有助于将商店的状态传递给应用程序中的所有容器组件,稍后我们将演示其工作原理。
The third line of code imports reducer
as formReducer
and that's what we're going to use to hook up our form to the Redux store.
第三行代码将reducer
作为formReducer
,这就是我们用来将表单连接到Redux存储的内容。
Next, we'll create the actual Redux store and make sure it's applicable to all container components by using the Provider
component. Edit the src/index.js
file with the following code block.
接下来,我们将创建实际的Redux存储,并通过使用Provider
组件确保它适用于所有容器组件。 使用以下代码块编辑src/index.js
文件。
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { reducer as formReducer } from 'redux-form';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
const rootReducer = combineReducers({
form: formReducer,
});
const store = createStore(rootReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
In the code block above, we use the combineReducers
function to connect the formReducer
from the form to the Redux store. It's basically used to update any of our state in response to actions, which in this case, are changes to the form. The next line of code is used to create a store by using createStore
from Redux.
在上面的代码块中,我们使用combineReducers
函数将formReducer
从表单连接到Redux存储。 它基本上用于更新我们的任何状态以响应动作,在这种情况下,就是对表单的更改。 下一行代码用于通过使用Redux中的createStore
创建商店。
This newly created store is then made available to all parts of the app with the help of Provider
which is wrapped around the App
component and it also accepts a prop of store which is the store
that was created above.
然后,在Provider
的帮助下,该新创建的商店可用于应用程序的所有部分,该Provider
包裹在App
组件周围,并且它还接受商店的道具,即上面创建的store
。
Let's go back to the form and finally connect it to the store.
让我们回到表格,最后将其连接到商店。
将表单连接到redux-form (Connecting the Form to redux-form)
We have our form component but it's not connected to redux-form
yet. Let's fix that. Type out this code block below just before the class App extends Component
and immediately after the declaration of the SignInForm presentational component.
我们有表单组件,但尚未连接到redux-form
。 让我们修复它。 在class App extends Component
和SignInForm演示组件声明之后立即在下面键入此代码块。
SignInForm= reduxForm({
form: 'signIn',
})(SignInForm);
In the code block above, SignInForm
is made into a redux-connected form using the reduxForm
Higher Order Component. This means that our form is now hooked up to the store. One thing to note is the config key form
, it is used as an identifier and it's used to provide a unique name for the form component. If they were multiple forms, then you'd need to use separate names so as to better manage their different states.
在上面的代码块, SignInForm
制成使用一个终极版,连接形式reduxForm
高阶分量。 这意味着我们的表单现在已连接到商店。 要注意的一件事是config key form
,它用作标识符,并且用于为form组件提供唯一的名称。 如果它们是多种形式,那么您需要使用单独的名称,以便更好地管理它们的不同状态。
The next thing we have to do is configure what happens when we click on the Submit button. In an ideal app, you'd want to send data to a remote API or some databases but for the purpose of demonstrations, we'll simply log the form data into the browser console. To do that we'll need to get the form data from the props and store them somewhere.
接下来要做的是配置单击“提交”按钮时发生的情况。 在理想的应用程序中,您希望将数据发送到远程API或某些数据库,但是出于演示的目的,我们仅将表单数据记录到浏览器控制台中。 为此,我们需要从道具中获取表单数据并将其存储在某处。
Inside the SignInForm
component, add the line of code below just above the return
statement.
在SignInForm
组件内部,在return
语句上方的下面添加代码行。
const { handleSubmit } = props;
return <form **onSubmit={handleSubmit}** className="form">
The props
in the SignInForm
form is destructured into handleSubmit
. The handleSubmit
function will then be used in the form as a handler for the onSubmit
event when the submit button is clicked on.
SignInForm
形式的props
被分解为handleSubmit
。 然后,当单击“提交”按钮时, handleSubmit
函数将在窗体中用作onSubmit
事件的处理程序。
Finally, inside the App
component in the src/App.js
file, we'll create a function that logs the form data to the browser console. Add the code block below to the file just before the render()
function.
最后,在src/App.js
文件的App
组件内部,我们将创建一个将表单数据记录到浏览器控制台的函数。 将下面的代码块添加到文件中render()
函数之前。
handleSignIn = values => {
console.log(values);
};
Then add the handleSignIn
function as an event handler for the SignInForm
component. redux-form
automatically will ascertain that the data gotten from the form, which is essentially the SignInForm
component should be logged to the console thanks to the handleSubmit
function above.
然后将handleSignIn
函数添加为SignInForm
组件的事件处理程序。 由于上面的handleSubmit
函数, redux-form
自动确定从表单(本质上是SignInForm
组件)获取的数据应该记录到控制台。
<SignInForm onSubmit={this.handleSignIn} />
You can now start the app by running the npm start
terminal command. Fill out the form, click on submit and you should the values logged to the console.
现在,您可以通过运行npm start
terminal命令来启动应用程序。 填写表格,单击提交,您应该将值记录到控制台。
![](https://scotch-res.cloudinary.com/image/upload/w_auto,q_auto:good,f_auto/media/53535/PouFLikURIqTSYFjUxev_Screen%20Shot%202018-02-25%20at%203.46.35%20AM.png)
添加验证 ( Adding Validations )
Validations are important when it comes to building forms and redux-form
ships with some validation features and we're going to implement that now. In the src/App.js
file, type in the code block below.
在构建具有某些验证功能的表单和redux-form
船时,验证非常重要,我们现在将实现它。 在src/App.js
文件中,键入下面的代码块。
const validate = val => {
const errors = {};
if (!val.firstName) {
console.log('First Name is required');
errors.firstName = 'Required';
}
if (!val.lastName) {
console.log('Last Name is required');
errors.lastName = 'Required';
}
if (!val.email) {
console.log('email is required');
errors.email = 'Required';
} else if (!/^.+@.+$/i.test(val.email)) {
console.log('email is invalid');
errors.email = 'Invalid email address';
}
if (!val.age) {
errors.age = 'Required'
} else if (isNaN(Number(val.age))) {
errors.age = 'Must be a number'
} else if (Number(val.age) < 18) {
errors.age = 'Sorry, you must be at least 18 years old'
}
return errors;
};
The validate
function is used to check for validation errors in the form. The val
parameter will be used to check the validation of different fields. We first check if the errors
object is empty, an empty object obviously means there are no errors. We then use conditional logic to check if a field is empty, if it is, you throw the corresponding error. In the code block above, we're only doing validations for firstName
, lastName
, email
, and age
. In the email
validation conditional, we check if it's empty and also check if it's a valid email by using regex
and in the age
validation conditional, we check if it's empty, a number and if the user is less than 18.
validate
函数用于检查表单中的验证错误。 val
参数将用于检查不同字段的有效性。 我们首先检查errors
对象是否为空,一个空对象显然意味着没有错误。 然后,我们使用条件逻辑来检查字段是否为空,如果为空,则抛出相应的错误。 在上面的代码块中,我们仅对firstName
, lastName
, email
和age
进行验证。 在email
验证条件中,我们使用regex
检查它是否为空,还检查它是否为有效电子邮件;在age
验证条件中,我们检查其是否为空,一个数字以及用户是否小于18。
Next, we'll register our validation function to redux-form
so that it can begin using it to carry out validation tests. Add the validate
function to the redux-form
Higher Order Component as seen below.
接下来,我们将验证功能注册到redux-form
以便它可以开始使用它来执行验证测试。 如下所示,将validate
函数添加到redux-form
高阶组件中。
SignInForm= reduxForm({
form: 'signIn',
validate,
})(SignInForm);
Now that we have our validation function ready and registered in the redux-form
HOC, how do we use it in our forms? Simple. By creating a reusable component that displays errors whenever there are any and using that newly created component in our forms.
现在,我们已经准备好验证功能并在redux-form
HOC中注册了,我们如何在表单中使用它? 简单。 通过创建一个可重用的组件,该组件在出现错误时显示错误,并在表单中使用该新创建的组件。
const renderField = ({ input, label, type, meta: { touched, error, warning } }) => (
<div>
<div className="control">
<label className="field">{label}</label>
<input className="input" {...input} placeholder={label} type={type}/>
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
</div>
)
The renderField
component takes in props of the input
object, a label
, the type
of input and meta
which is a redux-form
property. The line {touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
simply means that an error message should show if there are any errors when the form field has been clicked/focused on. Also, the form will not be submitted if there are any errors.
renderField
组件接受input
对象的属性, label
,输入type
和meta
,后者是redux-form
属性。 行{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
仅表示错误消息应显示是否存在单击/重点放在表单字段时发生错误。 另外,如果有任何错误,将不会提交表格。
Now, if you check the form and try to enter any invalid input or skip the fields that have validation tests, you should error messages underneath the form.
现在,如果您检查表单并尝试输入任何无效输入或跳过具有验证测试的字段,则应该在表单下方显示错误消息。
![](https://i-blog.csdnimg.cn/blog_migrate/7aa3cfafd15716ded605978992bab568.png)
结论 ( Conclusion )
In this tutorial, we've seen how to build forms with redux-form
and make it connected to the Redux store. We also saw how to implement synchronous validation on the form without the need for external schema validators or anything.
在本教程中,我们已经看到了如何使用redux-form
构建表单并将其连接到Redux存储。 我们还看到了如何在表单上实现同步验证,而无需外部模式验证器或其他任何东西。
You can read more about redux-form here and you can go through their several examples and learn how redux-from can be helpful for your next project.
您可以在此处阅读有关redux-form的更多信息,并阅读它们的几个示例,并了解redux-from如何为您的下一个项目提供帮助。
The codebase for this tutorial can be seen on GitHub.
翻译自: https://scotch.io/tutorials/managing-form-state-in-react-with-redux-form