How to implement Redux Form with RXJS

Before you see this article, you need to master the knowledge:

  • React
  • RXJS (at least Need to know what Subject is)
Background

FORM can be said to be one of the biggest problems in Web development. Compared to ordinary components, Form has the following features:

1, more users interact.

This means that a large number of custom components may be required, such as DataPicker, Upload, AutoComplete, and more.

3, frequent state changes.
Whenever the user enters a value, it may change the application status to update the form element or display the error message.

3, the form check, that is, verifies the validity of the user input data.

Form verification has a lot of form, such as input side verification, verification after loss of focus, or verify before submitting forms.
4, asynchronous network communication.

When the user inputs and asynchronous network communications, things that need to be considered are more. For example, AutoComplete, you need to get the corresponding data according to the user’s input, if the user initiates a request, it will cause a lot of waste to the resource. Because each input is

asynchronous
, the data that has been entered two times in the continuous user input will also exist “post-first until” issues.

It is because of these features, making the development of FORM difficult. In the next chapter, we combine RXJS and FORM to help us solve these problems better.
HTML FORM

Let us first refer to the native HTML Form before realizing our own FORM components.

Save Form Status

For an FORM component, you need to save information of all form elements (such as Value, Validity, etc.), HTML Form is no exception.

Where is the HTML FORM save the form status? How can I get form element information?

There are mainly the following methods:

Document.Forms will return all


form nodes.

HTMLFORMEMENT.ELEMENTS returns all form elements.

Event.Target.elements can also get all form elements.

  • Document.Forms [0] .Elements [0] .value; // Get the value of the first form element in the first Form const form = Document.QuerySelector (“form”); form.eferences [0] .value; form.addeventListener (‘Submit’, Function (Event) {Console.log (event.target.eferences [0] .value);});
  • Validation
  • The type of form check is generally divided into two:
  • Built-in form verification. The default will be automatically triggered when the form is submitted. By setting
Novalidate
property, you can close the automatic check of your browser. JavaScript checks.
   

The problem

is difficult to customize. For example, INLINE VALIDATION, only Submit can verify the form, and the style of Error Message cannot be customized.

It is difficult to address complex scenes.

, such as nested, etc.
The behavior of the INPUT component is not uniform, so that the value of the form element is difficult to obtain the value of the form element.
 If Checkbox and Multiple SELECT, you can't take Value directly when you take the value, you also need additional conversion.    VAR $ form = document.QuerySelector ('form'); function getformvalues ​​(form) {var value = {}; var elements = form.elements; // ELEMTNS Is An Array-Like Object for (VAR i = 0; i     React Rx Form  
I am classmate Go and see the source code https://github.com/reeli/react-rx-form

RXJS is a very powerful data management tool, But it does not have the function of user interface rendering, but React is particularly good at processing interfaces. Why not combine their strengths? Use React and RXJS to solve our FORM problem. Since they know their respective strengths, the division of labor is also CompareIt is more clear:

RXJS is responsible for managing status, and React is responsible for rendering the interface.

Unlike the Redux Form, we don’t store the FORM status in the Store, but directly save

In the component. Then use rxjs to notify each

, then
  assembly will determine if you need to update the UI according to data, you need to update < elements.length; i++) {
  var input = elements[i];
  if (input.name) {
   switch (input.type.toLowerCase()) {
    case 'checkbox':
     if (input.checked) {
      values[input.name] = input.checked;
     }
     break;
    case 'select-multiple':
     values[input.name] = values[input.name] || [];
     for (var j = 0; j < input.length; j++) {
      if (input[j].selected) {
       values[input.name].push(input[j].value);
      }
     }
     break;
    default:
     values[input.name] = input.value;
     break;
   }
  }

 }

 return values;
}

$form.addEventListener('submit', function(event) {
 event.preventDefault();
 getFormValues(event.target);
 console.log(event.target.elements);
 console.log(getFormValues(event.target));
}); setState 
Otherwise, nothing.
For example, it is assumed that there are three fields in an Form (as follows), when only the value of Fielda changes, in order not to let

and its sub-assembly, RE-render, REDUX The internal information needs to be restricted by ShouldComponentUpdate ()

.

// Pseudo code

RXJS can control the particle size of the component update to the smallest, in other words, it is to let

Re-Render that truly Re-Render, without re-rendering.

The core is Subject From the above design ideas, the following two problems can be summarized: Form and Field are a couple The relationship, the status of Form needs to be notified to multiple field. Field needs to modify the status of the component based on the data. The first problemIt is required to be an OBSERVABLE function, and it is an Observable that supports multicast. The second question is required to be an OBServer function. In RXJS, it is both OBSERVABLE and OBServer, and can also achieve multicast, isn't it Subject? Therefore, when implementing FORM, it will be used in large quantities.

FormState Data Structure


A State is also required in the Form component to save all Field status, this state is FormState. What should the structure of FormState be defined? In the earliest version, the structure of

FormState
is a look at the surface:
    Interface iformState {[fieldname: String ]: {DIRTY ?: Boolean; visited ?: boolean; error ?: TERROR; Value: string;};}   FormState is an object, It is  FieldName  to Key to use an object that saves the field as it as its Value. 
It seems that there is no problem right?
But. . . . .

The structure of final FormState became the following:

interface iformState {Fields: {[fieldname: string]: {dirty?: boolean; touch ?: boolean; error ?: string | undefined;};}; value;};}

  • Note: Filed Value does not contain FileD Value, only some status information of Field. There is only Field Values ​​in Values.
  • Why? ? ?
In fact, when the most basic Form and Field components are realized, the above two data structures are feasible.

Where is the problem?

Here first, you can only know what the data structure of FormState is likely to know. Data flow

In order to better understand the data stream, let’s look at a simple example. We have a FORM component that includes a Field component internally, and a text input is included inside the Field component. The data stream may be like this:

The user enters a character in the input box.

Input’s onchange event will be Trigger. Field’s onchange action will be by Dispatch.

Modify FormState based on Field’s OnChange Action.
 The FORM State update will notify the Field observer.   Field's observer will come out of the current field, if you find moreNew setState, if there is no update, nothing. 
SetState will make the field returner, the new Field Value can notify INPUT.
Core Components

First, we need to create two basic components, a Field component, an FORM component.

Field Components

The Field component is an intermediate layer that connects the FORM component and a form element. Its role is to let the INPUT components are more single. After you have it, INPUT only needs to make a display, you don’t need to care about other complex logic (Validate / Normalize, etc.). Moreover, for the INPUT component, not only in the Form component, it can also be used outside the FORM component (some places may not require logic to logic), so the abstraction of the layer of Field is still very important.

Intercept and conversion. Format / Parse / Normalize.
Form Check. Refer to HTML Form’s form check, we can put the validation in the field component, adapt to different needs by combining verification rules.
 Triggered the change in the Field state (such as Touched, Visited)   to provide the information required to the child component. Provide Field's Status (Error, Touch, Visited ...), and a callback function (onchange, onbrur ...) for forming element binding events (ONCHANGE, ONBLUR ...). 
With the characteristics of RXJS to control the update of the Field component, reduce unnecessary retren.

Communication with Form. FielWhen the D state changes, the FORM needs to be notified. Change the status of a field in Form, you also need to notify Field.

FORM component

Manage form status. The FORM component provides the form status to the field, and the Form is notified when the Field changes.

Provides FormValues.

When the format is failed, the form is committed.

Notifying the Field of each Form State. A FormSubject & Dollar will be created in the Form; each Form State change will send a data to FormSubject & Dollar; each Field will be registered as a viewer of FormSubject & Dollar; That is to say, Field knows every change in Form State, so it can be decided to update when appropriate.

When the formaction changes, the Field is notified. For example, when StartSubmit.

Communication between components 如何用RxJS实现Redux Form

1, FORM, and FIELD communications.

Context is mainly used for cross-level components. In actual development, Form and Field may be cross-level, so we need to use Context to ensure communication with Form and Field. Form is provided to Field via context.

2, Field and Form communications.
    The FORM component provides a D__ISPATCH__ method to the field component for communicating with Field and Form. All Field’s status and values ​​are made from form unified management. If you expect to update the status or value of a Field, you must have the corresponding action of DISPATCH.
  • 3, form elements, and communications Field
  • Field form elements and communicate mainly through the callback function. Field will provide the form element with callback functions such as Onchange, OnBlur.
  • interface design
  • for interface design, the simple and clear is very important. So Field retain only the necessary attributes, not the other attributes form elements required to pass on through by Field, but to the form elements to define themselves.
  • With Child Render, the corresponding state and method will be provided to sub-components, structures, and hierarchical classes.
  • Field:

type TValidator = (value: string | boolean) => string | undefined; interface IFieldProps {children: (props: IFieldInnerProps) = > React.ReactNode; name: string; defaultValue ?: any; validate ?: TValidator | TValidator [];}

Form:

  • interface IRxFormProps {children: (props: IRxFormInnerProps) => React.ReactNode; initialValues ​​?: {[fieldName: string]: any;}}
  • Here, a one of the most basic form is completed. Next we will make some extensions on its basis to meet more complex business scenes.
  • Enhance
  • FieldArray

FIELDARRAY is primarily used to render multiple groups of Fields.

Go back to our previous question, why do you want to divide FormState into FileDs and Values?

In fact, the problem is in FieldArray,
  • Initial length Decided by INITLENGTH or FORMVALUES.
  • FormState is updated.
  • Formvalues ​​
With RXJS, we control the particle size of the Field update to the smallest, that is, if a field value changes, it will not cause Form Components and other Feild Components Rerence.
Since Field can only perceive your Value change, then the problem is coming, how to implement the link between Field?

So the FormValues ​​component came into being.

Whenever FormVALUES changes, the FormValues ​​component notifies subcomponents of the new FormVALUES. That is to say, if you use the FormValues ​​component, each FormValues ​​change causes the FormValues ​​component and its sub-component Rerence, so it is not recommended to use a wide range of use, otherwise the performance problem may result.

In short, when using FormValues, it is best to put it in a place where it affects. Means ofWhen FormVALUES changes, the as few components as possible are retrer.

In the following code, Fieldb’s display needs to be judged according to Fielda’s value, then you only need to use FormValues ​​to Fielda and Fieldb.

{({FormValues, UpdateFormValues}) => (

{!! FormValues.a &&

}

) }

Formsection

FormSection is mainly used to reach a set of fields groups for multiplexing Reuse in the form. Mainly by adding a prefix to

Name

.

How do you add a prefix to the Name of Field and FieldArray?
I first thought of getting the Name of the sub-assembly through React.children, and then spliced ​​with the Formsection’s Name.
 However, FormSection and Field may not be a father and child relationship! Because the Field component can also be drawn into a separate component. Therefore, there is a problem of cross-level component communication.   Yes! Cross-class communication us will still use context. However, here we need to get the corresponding context value from FormConsumer, and then provide Prefix to consumer via Provider. At this time, Field / FieldArray is Former through consumer.The value provided in the section is not provided by the Provider of the Form component. Because Consumer will consume the value provided by the VVIDER that you have recently. 
{(FormContextValue) => {RETURN (

{children}

);}}
   
Unit test

is mainly used for tool classes.

Integration Test

is mainly used for components such as Field, FieldArray. Because they cannot be separated from Form, they cannot use unit testing. Note: In the test, you cannot directly modify an attribute on Instance, so that React sets the nodes above the PrOPS to Readonly (via the Object.defineProperty method). However, you can set the PROPS by overall setting.

Instance.props = {… Instance.props, SubscribeformAction: MockSubscribeformAction, Dispatch: MockDispatch,};

  • If there are too many forms in the project, then it is undoubtedly a burden for QA testing. At this time we hope to have a tool that automatically fill the form, help usHigh test efficiency.
When writing this tool, we need to simulate the Input event.

INPUT.VALUE = ‘v’; const Event = new event (‘input’, {bubbles: true}); input.dispatchevent (event);

Our expectation is to simulate DOM’s INPUT events through the code above, and then trigger the React’s ONChange event. But React’s onchange event is not triggered. Therefore, Value cannot be set to the INPUT element.

Because ReactDom has a logical when simulating the onchange event: only when the value of the INPUT changes, ReactDom will generate an onchange event.

React 16+ will overwrite the Input Value Setter, and you can refer to ReactDom’s InputRacking. So we only need to get the original value setter, and Call calls.

const nativeInputValueSetter = Object.getOwnPropertyDescriptor (window.HTMLInputElement.prototype, “value”) set;. NativeInputValueSetter.call (input, “v”); const event = new Event (“INPUT”, {bubbles: true}; input.dispatchevent (event);

 DEBUG   Print LOG    In the DEV environment, DEBUG can be performed by log. Currently printing logs automatically in the DEV environment, other environments do not print log.  Log's information mainly includes: prevstate, action, nextstate.  Note: Due to prevstate, action, nextstate is Object, don't forget to call CloneDeep when printing, otherwise it is not possible to ensure the correctness of the value of the last printed, that is, the final result may not The value of the moment of printing.  

This article only talked about the idea of ​​React Rx Form and some core technologies, you can also implement a version according to this idea. Of course, you can also refer to the source code, welcome to advice and Issue. GitHub Address: https://github.com/reeli/react-rx-form/reeli/react-rx-form/reeli/react-rx-form The above is all of this article, I hope to help everyone, I hope everyone will support Tumi Cloud.

© Copyright Notice
THE END
Just support it if you like
like0
share
comment Grab the couch

Please log in to comment