使用Redux表单在React中管理表单状态

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应用。

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确定应该是什么类型的输入字段,可以是inputtextareaselect标签,而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.

第一行代码导入createStorecombineReducerscreateStore帮助创建一个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导入ProviderProvider有助于将商店的状态传递给应用程序中的所有容器组件,稍后我们将演示其工作原理。

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命令来启动应用程序。 填写表格,单击提交,您应该将值记录到控制台。

添加验证 ( 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对象是否为空,一个空对象显然意味着没有错误。 然后,我们使用条件逻辑来检查字段是否为空,如果为空,则抛出相应的错误。 在上面的代码块中,我们仅对firstNamelastNameemailage进行验证。 在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 ,输入typemeta ,后者是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.

现在,如果您检查表单并尝试输入任何无效输入或跳过具有验证测试的字段,则应该在表单下方显示错误消息。

结论 ( 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.

本教程的代码库可以在GitHub找到

翻译自: https://scotch.io/tutorials/managing-form-state-in-react-with-redux-form

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值