React Redux technology stack — detailed explanation of Redux form

Time:2021-9-23

There is no two-way data binding like angular in react. When making some background pages with complex forms, the coding during monitoring, assignment, transmission and verification is relatively complex, and the full screen template code is extremely painful. Therefore, it is introduced to solve these problemsredux-form(V6) module. This article roughly translated some important parts of the official documents, added some features in combination with the official demo, and optimized some places where the official can’t run.

start

in useredux-formBefore, you need to have the following Foundation:

aboutredux-formThere are three main modules:

  • formReducer reducer: various forms are operated in the form of a Redux action. This reducer is used to promote the change of Redux store data.

  • reduxForm() HOC: this high-level component is used to integrate the user interaction bound by Redux action with your component, and return a new component for use.

  • <Field/>: replace your original with this<input/>Component, which can be connected with the logic of Redux form.

Data flow:

In most cases, you don’t need to care about how to create an action, everything is automatic. The following figure shows a simple data flow:

React Redux technology stack -- detailed explanation of Redux form

For a simple example, we have areduxForm()Create a form component with<Field/>Created<input/>Component, the data flow looks like this:

  1. The user clicks this<input/>Components,

  2. “Focus action” is triggered,

  3. Formreducer updates the corresponding status,

  4. This status is returned<input/>Component.

Similar in this<input/>The above process is also followed for entering text, changing status and submitting forms.

redux-formIt can also handle many things based on this process, such as form verification and formatting, multi parameter and action creation. Based on the following wizard, please self mine deeper functions.

Basic usage Wizard

Step 1 / 4: form reducer

The store needs to know how the component sends an action, so we need to register in your storeformReducer, it can serve all the form components defined by you in the whole app, so you only need to register once.

import { createStore, combineReducers } from 'redux'
import { reducer as formReducer } from 'redux-form'

const rootReducer = combineReducers({
  // ...your other reducers here
  // you have to pass formReducer under 'form' key,
  // for custom keys look up the docs for 'getFormState'
  form: formReducer
})

const store = createStore(rootReducer)

Note: the key of formreducer merged in reducer must be named “form”. If you need to customize the key for some reason, please move togetFormState configView details.

Step 2 / 4: form component

In order for your form component to interact with the store, we need to use the functionreduxForm()To wrap your components. It can provide the state in the form by props when you submit the form and other operations.

import React from 'react'
import { Field, reduxForm } from 'redux-form'

let ContactForm = props => {
  const { handleSubmit } = props
  return (
    <form onSubmit={ handleSubmit }>
      { /* form body*/ }
    </form>
  )
}

ContactForm = reduxForm({
  // a unique name for the form
  form: 'contact'
})(ContactForm)

export default ContactForm;

Now that we have a form component, let’s add some input components.

Note: if you find the syntax of () () confusing, you can look at it in two steps:

// ...

// create new, "configured" function
createReduxForm = reduxForm({ form: 'contact' })

// evaluate it for ContactForm component
ContactForm = createReduxForm( ContactForm )

export default ContactForm;
Step 3 / 4: form<Field/> Components

<Field/>The component can connect the data of all input type components to the store. The basic usage is as follows:

<Field name="inputName" component="input" type="text" />

It creates a text type<input/>Components, such asvalue onChange onBlurAnd other properties, which are used to track and maintain various states of this component.

Note:<Field/>Components are very powerful. In addition to basic types, you can also configure classes or stateless components. For more information, please move toField usage

import React from 'react'
import { Field, reduxForm } from 'redux-form'

const ContactForm = props => {
  const { handleSubmit } = props
  return (
    <form onSubmit={ handleSubmit }>
      <div>
        <label htmlFor="firstName">First Name</label>
        <Field name="firstName" component="input" type="text" />
      </div>
      <div>
        <label htmlFor="lastName">Last Name</label>
        <Field name="lastName" component="input" type="text" />
      </div>
      <div>
        <label htmlFor="email">Email</label>
        <Field name="email" component="input" type="email" />
      </div>
      <button type="submit">Submit</button>
    </form>
  )
}

ContactForm = reduxForm({
  // a unique name for the form
  form: 'contact'
})(ContactForm)

export default ContactForm;

From now on, the operation data on the form can be filled into the store and the form submission operation can be performed.

Step 4 / 4: reacting to submit

The submitted data is injected into the form component as a JSON objectonSubmitIn the method, you can print it out:

import React from 'react'
import ContactForm from './ContactForm'

class ContactPage extends React.Component {
  submit = (values) => {
    // print the form values to the console
    console.log(values)
  }
  render() {
    return (
      <ContactForm onSubmit={this.submit} />
    )
  }
}

Life cycle of the form

This section is for understanding your component valueredux-formThe direction of flow is important

Value lifecycle hook function

redux-formThree value life cycle hook functions are provided, which are passed to the field component through props, and they are all optional.

format(value:Any) => String

Format the data from the store and render it to the component. Usually, the original data type will be retained in the store and formatted only when used in the component.

parse(value:String) => Any

Format the string data entered by the user and put it into the store for your use. The converted type data will also be retained in the store.

normalize(value:Any, previousValue:Any, allValues:Object, previousAllValues:Object) => Any

Logic that allows you to add some constraints to the current field data, such as constraintsmidDateDate ofmaxDateWait before. If you add these logic, throughnormalize()The value of will be parsed.

Value lifecycle

React Redux technology stack -- detailed explanation of Redux form

API

Due to space limitations, only the common usage methods of each API are listed here. Please move to the next stepOfficial API documentation

reduxForm(config:Object)

Create a decorator that allows you to configure your form by configuring some parameters. Such as how to configure form validation, callback for successful or failed submission, action sending for obtaining or losing focus, prop namespace, etc. specific examples will be introduced in the subsequent demo.

Importing
var reduxForm = require('redux-form').reduxForm;  // ES5

import { reduxForm } from 'redux-form';  // ES6
Introduction to common parameters

Necessary parameters

  • form : String[required]: used to name your form and generate this named data node in the store.

Optional parameters

  • onChange : Function [optional]: callback after the form triggers the onchange event.

  • onSubmit : Function [optional[: form submission configuration. You can configure which parameters need to be submitted, as well as the parameters triggered during submissiondispatchWait.

  • onSubmitSuccess : Function [optional] & onSubmitFail : Function [optional]: callback for successful and failed form submission.

  • shouldValidate(params) : boolean [optional]: synchronous verification.

  • shouldAsyncValidate(params) : boolean [optional]: asynchronous authentication.

  • touchOnBlur : boolean [optional] & touchOnChange : boolean [optional]: identificationonBluroronChangeTrigger of.

props

List all current pages byredux-formGenerate props to decorate this form component.

If you want to write proptypes in strict mode,redux-formAll proptypes here will be exported. You need to reference them and add your own proptypes, like this:

import {reduxForm, propTypes} from 'redux-form';

class SimpleForm extends Component {
  static propTypes = {
    ...propTypes,
    // other props you might be using
  }
  // ...
}
Common properties
  • pristine : trueIndicates that the form data is original data and has not been modified; otherwisedirty

  • submitting: used to indicate your form submission status. It will only return one after your form is submittedpromiseObject.falseexpresspromiseObject isresolvedorrejectedStatus.

  • handleSubmit(eventOrSubmit) : Function: the function that submits the form. If the form needs validation, the validation method will be executed (including synchronous and asynchronous). There are two methods to call:

    • Component internal direct call<form onSubmit={handleSubmit}>

    • Assign to prop external call<MyDecoratedForm onSubmit={data => {//do something with data.}}/>

Field

All you need to do withstoreData connection form components can be used<Field/>。 There are three basic concepts you need to understand before using it correctly:

  1. Must containnameProperties. It can be a simple string, such asuserNamepassword, it can also be a complex structure, such ascontact.billing.address[2].phones[1].areaCode

  2. Must containcomponentProperties. It can be a component, a stateless component, or the default tags (input, textarea, select) supported by the dom.

  3. All other attributes are passed to the element generator through prop. asclassName

Importing
var Field = require('redux-form').Field;  // ES5

import { Field } from 'redux-form';  // ES6
usage method

1. Components

Can be any customclassOther third-party libraries for components.

// MyCustomInput.js
import React, { Component } from 'react'

class MyCustomInput extends Component {
  render() {
    const { input: { value, onChange } } = this.props
    return (
      <div>
        <span>The current value is {value}.</span>
        <button type="button" onClick={() => onChange(value + 1)}>Inc</button>
        <button type="button" onClick={() => onChange(value - 1)}>Dec</button>
      </div>
    )
  }
}

Then use this:

import MyCustomInput from './MyCustomInput'

...

<Field name="myField" component={MyCustomInput}/>

2. Stateless components

This is a very flexible use<Field/>Methods of use andredux-formThe previous version of is very similar. But it must be in your heartrender()Method, otherwise it will be rebuilt every time it is rendered, and due to the complexity of the componentpropWill change, will force<Field/>Render. If you arerender()Defining stateless components internally will not only slow down your app, but also the component input will lose focus every time the component is re rendered.

// outside your render() method
const renderField = (field) => (
    <div className="input-row">
      <input {...field.input} type="text"/>
      {field.meta.touched && field.meta.error &&
       <span className="error">{field.meta.error}</span>}
    </div>
  )

// inside your render() method
<Field name="myField" component={renderField}/>

3.string: input, select, or textarea

For example, create a text input box component

<Field component="input" type="text"/>

Fields

AndFieldSimilar, but it uses multiple fields at the same time.<Fields/>staynameProperty instead of using a single form namenameProperty.

Important: please use it sparingly<Fields/>, when the data of any form component inside it changes, the whole will be re rendered<Fields/>。 Therefore, it will become the performance bottleneck of your app. Unless you really need to, you’d better use it<Field/>Customize your form components one by one

Importing
var Fields = require('redux-form').Fields;  // ES5

import { Fields } from 'redux-form';  // ES6
usage method

And<Field/>Almost. There are two usage modes, component and stateless component, which are not described in detail here.

FieldArray

This component allows you to define a series of forms, its working principle and<Field/>Same. adopt<Field/>, give it onename, you can map toRedux stateThe specified location in the. Components can also be connected toRedux stateYespropsRender.

adopt<FieldArray/>, you also need to<Field/>Give it onename。 And you inject<FieldArray/>The component of will receive a series of through the field arraypropsTo query, update, and iterate.

Importing
var FieldArray = require('redux-form').FieldArray;  // ES5

import { FieldArray } from 'redux-form';  // ES6
usage method

It will be described in detail later in the demo

Form

FormComponent simply encapsulates the form component of react to triggerredux-formModified componentonSubmitFunction.

You can use it in the following scenarios:

  • Inside your form component, you canonSubmit={this.props.handleSubmit(this.mySubmitFunction)}Execute your submission.

  • perhaps

If you justonSubmitFunction as your configuration or property, you don’t need this component.

Importing
var Form = require('redux-form').Form;  // ES5

import { Form } from 'redux-form';  // ES6
usage method

All you need to do is<form>replace with<Form>Just.

FormSection

FormSectionExisting form components can be easily divided into smaller components for reuse in complex forms. It is well defined byFieldFieldsandFieldArrayWord assemblynamePrefix to complete this function.

usage method

The business described in this example is an order user information form structure from the perspective of buyer and recipient. The buyer and the recipient have the same field structure, so split this part into a field namedPartyThe components are meaningful. Suppose nowPartycontaingivenName middleName surname addressThese fields, and thenaddressParts are broken down again into reusable componentsAddress。 The code is as follows:

//Address.js
class Address extends Component {
    render() {
        return <div>
            <Field name="streetName" component="input" type="text"/>
            <Field name="number" component="input" type="text"/>
            <Field name="zipCode" component="input" type="text"/>
        </div>
    }
}

//Party.js
class Party extends Component {
    render() {
        return <div>
            <Field name="givenName" component="input" type="text"/>
            <Field name="middleName" component="input" type="text"/>
            <Field name="surname" component="input" type="text"/>
            <FormSection name="address">
                <Address/>
            </FormSection>
        </div>
    }
}

//OrderForm.js
class OrderForm extends Component {
    render() {
        return <form onsubmit={...}>
            <FormSection name="buyer">
                <Party/>
            </FormSection>
            <FormSection name="recipient">
                <Party/>
            </FormSection>
        </form>
    }
}
//don't forget to connect OrderForm with reduxForm()

The complete name of the field will eventually becomebuyer.address.streetNameThe result structure is as follows:

{
    "buyer": {
        "givenName": "xxx",
        "middleName": "yyy",
        "surname": "zzz",
        "address": {
            "streetName": undefined,
            "number": "123",
            "zipCode": "9090"
        }
    },
    "recipient": {
        "givenName": "aaa",
        "middleName": "bbb",
        "surname": "ccc",
        "address": {
            "streetName": "foo",
            "number": "4123",
            "zipCode": "78320"
        }
    }
}

similarAddressYour component rarely changes itsname, in order to make the component inheritFormSectioninstead ofComponent, you need to set a defaultnameAs follows:

class Address extends FormSection {
    //ES2015 syntax with babel transform-class-properties
    static defaultProps = {
        name: "address"
    }
    render() {
        return <div>
            <Field name="streetName" component="input" type="text"/>
            <Field name="number" component="input" type="text"/>
            <Field name="zipCode" component="input" type="text"/>
        </div>
    }
}
//Regular syntax:
/*
Address.defaultProps = {
    name: "address"
}
*/

formValues()

As a decoration, you can read the current formvalue。 When the list subcomponentonChangeIt is useful to rely on the values in the current form.

Importing
var formValues = require('redux-form').formValues;  // ES5

import { formValues } from 'redux-form';  // ES6
usage method
const ItemList = formValues('withVat')(MyItemizedList)

const ItemList = formValues({showVat: 'withVat'})(MyItemizedList)

These decorative components are now owned separatelywithVatAndshowVatYesprops

formValueSelector()

formValueSelectorAPI can be very convenientconnect() stateValue of to formvalueInside. It can be through the formnameCreate a for your formvaluePick up.

Importing
var formValueSelector = require('redux-form').formValueSelector;  // ES5

import { formValueSelector } from 'redux-form';  // ES6
usage method

First, you need to follow your formnameCreate aselector

const selector = formValueSelector('myFormName')

Then there are several ways to useselector:

1. Pick individual fields

connect(
  state => ({
    firstValue: selector(state, 'first'),
    secondValue: selector(state, 'second')
  })
)(MyFormComponent)

2. After groupingpropPick multiple fields as groups in

connect(
  state => ({
    myValues: selector(state, 'first', 'second')
  })
)(MyFormComponent)

3. PutselectorAsmapStateToPropsTo use

If you don’t needstateOther attribute values in,selectorAsmapStateToPropsThis work can be done automatically.

connect(
  state => selector(state, 'first', 'second')
)(MyFormComponent)

reducer

TabularreducerUsed to install yourRedux stateTo your form.

If you useImmutablejsTo manage yourRedux state, you have toredux-form/immutableImport inreducermodular.

Es5 example
var redux = require('redux');
var formReducer = require('redux-form').reducer;
// Or with Immutablejs:
// var formReducer = require('redux-form/immutable').reducer;

var reducers = {
  // ... your other reducers here ...
  form: formReducer
};
var reducer = redux.combineReducers(reducers);
var store = redux.createStore(reducer);
ES6 example
import { createStore, combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
// Or with Immutablejs:
// import { reducer as formReducer } from 'redux-form/immutable';

const reducers = {
  // ... your other reducers here ...
  form: formReducer
};
const reducer = combineReducers(reducers);
const store = createStore(reducer);

reducer.plugin

The form returns a function specified by attachingreducersTo acceptactionYesreducer。 Its parameter should be a mappingformNameAnd one(state, action) => nextState reducerAn object of a relationship. Through eachreducerThe state of can only be a fragment belonging to that form.

explain

fluxThe most beautiful part of the system should be allreducers(orFluxStandard terminology instores)All are acceptableactions, they can modify based on theseactionTo modify the data. For example, you have a login form. When you fail to submit, you want to know the data in the password input box, even if your login submission information belongs to another userreducer/actionsSystem, your form can still make its own response.

Instead of usingredux-formAn ordinaryreducer, you can callplugin()Function to strengthen yourreducer

Note: This is an enhanced operation to modify your internalredux-form stateIf you don’t use it carefully, you will screw things up.

example

The function of the following example is whenAUTH_LOGIN_FAILYesactionWhen distributed, you can clear the password input box in the login form:

import { createStore, combineReducers } from 'redux'
import { reducer as formReducer } from 'redux-form'
import { AUTH_LOGIN_FAIL } from '../actions/actionTypes'

const reducers = {
  // ... your other reducers here ...
  form: formReducer.plugin({
    login: (state, action) => {   // <----- 'login' is name of form given to reduxForm()
      switch(action.type) {
        case AUTH_LOGIN_FAIL:
          return {
            ...state,
            values: {
              ...state.values,
              password: undefined // <----- clear password value
            },
            fields: {
              ...state.fields,
              password: undefined // <----- clear field state, too (touched, etc.)
            }
          }
        default:
          return state
      }
    }
  })
}
const reducer = combineReducers(reducers)
const store = createStore(reducer)

SubmissionError

thisthrowable errorUsed fromonSubmitReturns a form validation error message. The purpose is to distinguishpromiseWhether the failure is due to validation errors, AJAX I / O errors, or other server errors. If it’s due to{ field1: 'error', field2: 'error' }The error will be added to each field marked with the error attribute, just like the asynchronous form validation error. If an error does not specify a field, but is applied to the whole form, you need to continue to pass it, as if it was called by a field_errorThen he will give a wrong attribute. (that is, whether he throws it out)

Importing
var SubmissionError = require('redux-form').SubmissionError;  // ES5

import { SubmissionError } from 'redux-form';  // ES6
usage method
<MyForm onSubmit={values =>
  ajax.send(values) // however you send data to your server...
    .catch(error => {
      // how you pass server-side validation errors back is up to you
      if(error.validationErrors) {
        throw new SubmissionError(error.validationErrors)
      } else {
        // what you do about other communication errors is up to you
      }
    })
}/>

Action Creators

redux-formOpening up all internalaction creators, allows you to complete the distribution as you wishaction Control of. Furthermore, the official recommends that you complete most of your requirements by specifying the required fields in those formsactionFor example, as theseactionAlready bound todispatchLike, just put theseactionadoptpropsPass.

specificactionPlease refer to the official documentation.

Selectors

redux-formProvides a series of usefulRedux statePicker, which can be picked in any form anywhere in the appstateData on.

All of the following pickups have a uniform method of use: they all (exceptgetFormNames)Use the name of the form to create a picker, regardless of the name of the formstateWhat is it?

import {
  getFormValues,
  getFormInitialValues,
  getFormSyncErrors,
  getFormMeta,
  getFormAsyncErrors,
  getFormSyncWarnings,
  getFormSubmitErrors,
  getFormNames,
  isDirty,
  isPristine,
  isValid,
  isInvalid,
  isSubmitting,
  hasSubmitSucceeded,
  hasSubmitFailed
} from 'redux-form'

MyComponent = connect(
  state => ({
    values: getFormValues('myForm')(state),
    initialValues: getFormInitialValues('myForm')(state),
    syncErrors: getFormSyncErrors('myForm')(state),
    fields: getFormMeta('myForm')(state),
    asyncErrors: getFormAsyncErrors('myForm')(state),
    syncWarnings: getFormSyncWarnings('myForm')(state),
    submitErrors: getFormSubmitErrors('myForm')(state),
    names: getFormNames('myForm')(state),
    dirty: isDirty('myForm')(state),
    pristine: isPristine('myForm')(state),
    valid: isValid('myForm')(state),
    invalid: isInvalid('myForm')(state),
    submitting: isSubmitting('myForm')(state),
    submitSucceeded: hasSubmitSucceeded('myForm')(state),
    submitFailed: hasSubmitFailed('myForm')(state)
  })
)(MyComponent)

Examples

Simple Form

This example lists all the basic elements of the form. The difference from the official demo is that two are addedtypebyfileYesField(directly atFieldUsed infileThere will be some problems with the type of), one is using jQuerydropifyComponent written to upload a single fileMyDropify, one is useddropzoneA component written to upload multiple filesMyDropzone(used here)react-dropzoneandredux-formCombination of). The official examples will not be introduced separately. We will mainly post two customized examplesField

Note: since reducer was designed as a pure function at the beginning, the final value obtained from the form submitting the file is a functionfileObject when you useredux-immutable-state-invariantDetection tools such aslastModifiedDateThe value of will report an error,For details, please see。 Here, we will not consider immutable for the time being.

Simple path

src/components/demo/simple/

MyDropify

src/components/utils/MyDropify.js

code:

import React, { Component } from 'react';
const $ = window.$;
require('dropify');

class MyDropify extends Component {
  componentDidMount(){
    $('.dropify').dropify();
  }
  render() {
    const { input,dataAllowedFileExtensions } = this.props
    const onAttachmentChange = (e) => {
        e.preventDefault();
        const files = [...e.target.files];
        input.onChange(files);
    };
    return (
      <div>
        <input type="file"
               onChange={onAttachmentChange}
               className="dropify"
               data-allowed-file-extensions={dataAllowedFileExtensions} />
      </div>
    )
  }
}

export default MyDropify;

usage method:

  <div className="form-group">
    <div className="input-group">
      <label>Dropify</label>
      <Field component={MyDropify}
             name="inputfile1"
             dataAllowedFileExtensions="doc docx txt pdf xls xlsx jpg png bmp"></Field>
    </div>
  </div>

dropifyPlease refer to its official documents for specific usage.

MyDropzone

src/components/utils/MyDropify.js

code:

import React, { Component } from 'react';
import Dropzone from 'react-dropzone';
class MyDropzone extends Component {
  render() {
    const { input,desc,accept } = this.props
    const onDrop = (files) => {
        input.onChange(files);
    };
    return (
      <Dropzone onDrop={onDrop} accept={accept}>
        {({ isDragActive, isDragReject, acceptedFiles, rejectedFiles }) => {
           if (isDragActive) {
             return "This file is authorized";
          }
           if (isDragReject) {
             return "This file is not authorized";
          }
           return acceptedFiles.length || rejectedFiles.length
             ? `Accepted ${acceptedFiles.length}, rejected ${rejectedFiles.length} files`
            : desc;
        }}
      </Dropzone>
    )
  }
}

export default MyDropzone;

usage method:

  <div className="form-group">
    <div className="input-group">
      <label>Dropzone</label>
      <Field component={MyDropzone}
             name="inputfile2"
             desc="My Dropzone"
             accept="image/png,image/jpeg"></Field>
    </div>
  </div>

react-dropzoneIt is different from the jQuery version and has been useddropzoneYou should know that the selected file can be rendered into the box, and the react versiondropzoneThe soundtrack does not have this function, but it provides a detailed method to realize many functions. For example, after selecting the file, it can be rendered to the component. I will improve this function when I have time.

Sync Validation

Synchronized form validation, including error and warning configuration. The official demo only demonstrates the verification of the input box, and here are prepared to includeradio select textareaVerification method of(checkboxI will explain it in a separate chapter), and the calling method can be seen in the source code of this article.

Sync validation path

src/components/demo/syncValidation/

radioField

src/components/utils/validation/radioField.js

import React from 'react';

const inputField = ({
  input,
  label,
  type,
  meta: { touched, error, warning }
}) => (
  <div className={touched && error ? 'has-error form-group':'form-group'}>
    <div className="input-group">
      <span className="input-group-addon">{label}</span>
      <input {...input} placeholder={label} type={type} className="form-control"/>
    </div>
    {touched &&
      ((error && <div className="help-block with-errors">{error}</div>) ||
        (warning && <div className="help-block with-errors">{warning}</div>))}
  </div>
)

export default inputField;
selectField

src/components/utils/validation/selectField.js

import React from 'react';
const selectField = ({
  input,
  label,
  selects,
  meta: { touched, error, warning }
}) => (
  <div className={touched && error ? 'has-error form-group':'form-group'}>
    <div className="input-group">
      <span className="input-group-addon">{label}</span>
      <select {...input} className="form-control">
        {
          selects.map((item, i) => (
            <option key={i} value={item.value}>{item.text}</option>
          ))
        }
      </select>
    </div>
    {touched &&
      ((error && <div className="help-block with-errors">{error}</div>) ||
        (warning && <div className="help-block with-errors">{warning}</div>))}
  </div>
)

export default selectField;
textareaField

src/components/utils/validation/textareaField.js

import React from 'react';

const textareaField = ({
  input,
  label,
  type,
  cols,
  rows,
  meta: { touched, error, warning }
}) => (
  <div className={touched && error ? 'has-error form-group':'form-group'}>
    <label>{label}</label>
    <textarea {...input} cols={cols} rows={rows} className="form-control"></textarea>
    {touched &&
      ((error && <div className="help-block with-errors">{error}</div>) ||
        (warning && <div className="help-block with-errors">{warning}</div>))}
  </div>
)

export default textareaField;

Field-Level Validation

In addition to providing a verification method to verify the values in the form together, you can also verify each<Field/>or<FieldArray/>Verify separately. The official demo is enough to explain the problem. Here is only for the aboveSync ValidationMake a simple rewrite. See the code for details.

Submit Validation

A better method of server form validation is to callonSubnitThen return arejectedYespromiseObject. When your form is submitted, there are 2 methods available to youredux-formThis function.

  1. Treat him as aonSubmitYespropDecorative components passed to you. In that case, you can use it in your decorative componentsonSubmit={this.props.handleSubmit}Make sure this function is triggered when the user clicks the submit button.

  2. Pass it as a parameter to the inside of your decoration componentthis.props.handleSubmitFunction. In this case, you need to useonClick={this.props.handleSubmit(mySubmit)}To ensure that this function is triggered when the user clicks the submit button.

This error message is displayed in the same way as the error message after synchronous validation, but it passesonSubmitFunction returns an encapsulatedSubmissionErrorObject. Like the 400 or 500 error of HTTP, this verification error is different from the I / O error, and it will be submitted by thispromiseThe state of the object is set torejected

There are no flower heads in demo. Like the official, it is based onSyncValidationPut the logic of form verification in the logic after submission, and throw aSubmissionError

Async Validation

The server form verification method is recommendedSubmit ValidationHowever, it is possible that when you fill out the form, you need to verify it on the server side. A classic example is when a user selects a value, such as user name, which must be the only value in your system.

In order to write an asynchronous form validation, you need toredux-formAn asynchronous validation function is provided to provide an object that can get data from the form, and thenReduxDistribute this function to return a status that has an error objectrejectsOr status isresloveYespromiseObject.

You need to specify several fields at the same timeasyncBlurFieldsTo mark whether they need to trigger this asynchronous verification when they lose focus.

important
  1. Asynchronous validation will beonSubmitCalled before, so if you careonSubmitVerification, you need to useSubmit Validation

  2. When the synchronous validation of a field is wrong, the asynchronous validation will not be triggered when it loses focus.

Customization in demo<Field/>YesmetaOne of themasyncValidatingTo identify the of asynchronous authenticationpromiseObjectPendingStatus.

Initialize From State

adoptinitialValuesAttribute orreduxForm()The data provided by the configured parameters is loaded into the formstateAnd take these initialization data as original data. Whenreset()When triggered, these values will also be returned. In addition to saving thesepristineThe operation of initializing your form will also replace the existing values in the form.

In many applications, these values may be from the server and stored in other locationsreducerIn. To get these values, you need to useconnect()Go to your own linkstateThen map the data to yourinitialValuesProperties.

By default, you only need to passinitialValuesInitialize your form component once. At present, there are two methods to pass the newpristineValue reinitializes the form.

  1. Pass aenableReinitializeProperty or configurationreduxForm()If the parameter in is true, the form can be displayed every timeinitialValuesReinitialize when the property changes to generate a new onepristineValue. If you want to keep the changed form values during reinitialization, you can setkeepDirtyOnReinitializeIs true. By default, reinitialization willpristineValue replaces the value of the changed form.

  2. Send aINITIALIZEActionredux-formAction generator (generated).

This demo is more than the official demoenableReinitializeandkeepDirtyOnReinitializeUsage of. The following is a code snippet.

InitializeFromStateForm = reduxForm({
  form: 'initializeFromState',// a unique identifier for this form
  enableReinitialize:true,
  Keepdirtyonreinitialize: true, // this value means that the changed value will not be replaced after the form is reinitialized. You can use clear to test
})(InitializeFromStateForm)

Selecting Form Values

Sometimes you want to access the values of some fields in the form component. You need tostoreMedium directconnect()The value of the form. In general use,redux-formadoptformValueSelectorA convenient selector is provided.

Warning: you need to use this mechanism sparingly, because if a value in the form changes, your component will be re rendered.

Code snippet:

// Decorate with reduxForm(). It will read the initialValues prop provided by connect()
SelectingFormValuesForm = reduxForm({
  form: 'selectingFormValues',// a unique identifier for this form
})(SelectingFormValuesForm)

// Decorate with connect to read form values
const selector = formValueSelector('selectingFormValues') // <-- same as form name
SelectingFormValuesForm = connect(state => {
  // can select values individually
  const hasEmailValue = selector(state, 'hasEmail')
  const favoriteColorValue = selector(state, 'favoriteColor')
  // or together as a group
  const { firstName, lastName } = selector(state, 'firstName', 'lastName')
  return {
    hasEmailValue,
    favoriteColorValue,
    fullName: `${firstName || ''} ${lastName || ''}`
  }
})(SelectingFormValuesForm)

export default SelectingFormValuesForm

Field Array

This example shows how to build a field group, including a field group with one field and a field group with a set of fields. In this form, each member of the club has a last name and first name, as well as a list of interests. The following are the operations of these arraysinsert, pop, push, remove, shift, swap, unshiftBehavior is allowed: (for more details, please refer toFieldArray Docs)

  • OneactionOriginal structure of

  • Through your formthis.props.arrayObject boundaction

  • Bind form and pass at the same timeFieldArrayThe of the array on the object obtained by the componentaction

Remote Submit

This example demonstrates how a form is sent from an unrelated component or middlewareSUBMITTo execute the submission logic.

The submit button you see in this example is not directly linked to the form component, but only throughReduxSent a submittedaction

Note how it works. This submission function must passreduxForm()Pass or pass configuration parameterspropProvided to form components. The following is how to send this action:

import React from 'react'
import { connect } from 'react-redux'
import { submit } from 'redux-form'

const style = {
  padding: '10px 20px',
  width: 140,
  display: 'block',
  margin: '20px auto',
  fontSize: '16px'
}

const RemoteSubmitButton = ({ dispatch }) => (
  <button
    type="button"
    style={style}
    onClick={() => dispatch(submit('remoteSubmit'))}
  >
    Submit
  </button>
)
//Remotesubmit is the name of the form
export default connect()(RemoteSubmitButton)

Field Normalizing

When you need user input andstoreApply some control between the data you can usenormalizernormalizerWhenever the value changes, it can be saved tostoreA function that performs some conversion before.

A common example: you need some formatted value, such as phone number or credit card number.

NormalizersFour parameters were passed:

  • value-You setnormalizerThe value of the field

  • previousValue-This value is the value before the last change

  • allValues-The current values of all fields in the form

  • previousAllValues-Values of all fields in the form before the last change

These allow you to restrict a specific field based on another field in the form. For example, the minimum and maximum value of the field in the example: you can’t set it hereminValue ratio inmaxThe value in is too large to be setmaxValue ratio inminThe value of is smaller (code below)

const upper = value => value && value.toUpperCase()
const lower = value => value && value.toLowerCase()
const lessThan = otherField => (value, previousValue, allValues) =>
  parseFloat(value) < parseFloat(allValues[otherField]) ? value : previousValue
const greaterThan = otherField => (value, previousValue, allValues) =>
  parseFloat(value) > parseFloat(allValues[otherField]) ? value : previousValue

The following is the logic of telephone number processing

const normalizePhone = value => {
  if (!value) {
    return value
  }

  const onlyNums = value.replace(/[^\d]/g, '')
  if (onlyNums.length <= 3) {
    return onlyNums
  }
  if (onlyNums.length <= 7) {
    return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3)}`
  }
  return `${onlyNums.slice(0, 3)}-${onlyNums.slice(3, 6)}-${onlyNums.slice(6, 10)}`
}

Wizard

A common UI design pattern is to divide a single form into several groups of separate form forms. The best known isWizard。 useredux-formThere are many ways to do this design, but the simplest and most recommended way is to follow the following instructions:

  • Connect each page with the same form namereduxForm()

  • appointdestroyOnUnmountbyfalseYou can save the form data when the form component is unloaded

  • You can specify a synchronization verification function for the entire form

  • useonSubmitTo trigger the next step because it forces the validation function to run

What you need to do yourself:

  • Called manually after a successful submissionprops.destory()

The code in the example mainly lists the controlWizardThe usage of other components has been well known to us.

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import WizardFormFirstPage from './WizardFormFirstPage'
import WizardFormSecondPage from './WizardFormSecondPage'
import WizardFormThirdPage from './WizardFormThirdPage'

class WizardForm extends Component {
  constructor(props) {
    super(props)
    this.nextPage = this.nextPage.bind(this)
    this.previousPage = this.previousPage.bind(this)
    this.state = {
      page: 1
    }
  }
  nextPage() {
    this.setState({ page: this.state.page + 1 })
  }

  previousPage() {
    this.setState({ page: this.state.page - 1 })
  }

  render() {
    const { onSubmit } = this.props
    const { page } = this.state
    return (
      <div>
        {page === 1 && <WizardFormFirstPage onSubmit={this.nextPage} />}
        {page === 2 &&
          <WizardFormSecondPage
            previousPage={this.previousPage}
            onSubmit={this.nextPage}
          />}
        {page === 3 &&
          <WizardFormThirdPage
            previousPage={this.previousPage}
            onSubmit={onSubmit}
          />}
      </div>
    )
  }
}

WizardForm.propTypes = {
  onSubmit: PropTypes.func.isRequired
}

export default WizardForm

Recommended Today

Seven Python code review tools recommended

althoughPythonLanguage is one of the most flexible development languages at present, but developers often abuse its flexibility and even violate relevant standards. So PythoncodeThe following common quality problems often occur: Some unused modules have been imported Function is missing arguments in various calls The appropriate format indentation is missing Missing appropriate spaces before and after […]