React form source code reading notes

front end 2021-02-23 04:03:46
react form source code reading


1 Concept

1.1 What is a form

In fact, forms in a broad sense are not very well defined , Wiki says that a form is a series of documents with spaces , Used to input or select . More specific , In web pages, forms are mainly responsible for data collection , All of the forms we mentioned below refer to the latter . As shown in the figure below Google Profile configuration page to change the name of the form :

1.2 Responsibilities of forms

Form through the appropriate UI & Interaction , Transforming user input into a specific data structure ,js It's usually an object in the transfer process, and then it's usually json, For example, the actual form data in the above example is very likely .

{
lastName: ' Han ',
firstName: ' red ',
}
 Copy code 

More specifically , The form mainly needs to solve the following common problems :

  1. Specific form items correspond to UI Components , For example, the input box , Drop down boxes and so on ;
  2. around UI Components , Its corresponding label And the overall layout and organization of error messages ;
  3. Form verification , It includes simple verification and dependency verification ;
  4. Representation of nested data , For example, a list of & object ;
  5. The linkage between fields .

2 React The official form scheme

React The official form scheme is very simple , Just look at the official documents reactjs.org/docs/forms.…. in general , There are two different forms offered by the government , Form implementation based on controlled components and uncontrolled components , Of course, the former is more common . All third-party forms can be regarded as the extension and encapsulation of these two solutions .

2.1 Controlled components

In short, it means that the state and callback function of the component are completely controlled by the parent component , The state change of the child component needs to be notified to the parent component through the return function , After the state change is completed by the parent component, the new value is returned to the child component .

In terms of code, it's like And so on value as well as onChange these two items. props To achieve control . Here's an official example :

class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
 Copy code 

2.1 Uncontrolled components

I just said that all the states of controlled components are taken over by the outside world , Uncontrolled components are the opposite , It stores the state inside itself , We can borrow React Medium ref To access it . It's also an official example :

class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
 Copy code 

2.3 With which

About controlled vs Uncontrolled choice , I looked up a lot of information , Most documents think that controlled mode should be given priority , Unfortunately, we still haven't found a convincing reason . A large number of documents are nothing more than enumeration, which is more flexible than uncontrolled , more React Single data stream , It feels more like looking at it for political correctness than for real development experience . There's even a widely circulated comparison :

I personally think there's something wrong with this picture , Some of the scenarios listed below cannot be covered by uncontrolled components , actually ref Matching components onChange It can be done , For example, field level real-time verification , We can put a listening function on the field , Check the field when its value changes , And then through ref Control the internal state of the component . So I don't think the above scenario is a reason for not advocating uncontrolled components .

We've talked about this before , There is an interesting saying that :

In fact, the crux of the problem is not react, And lies in react The idea behind the realization is ViewModel. As it should be ,react core team Want to use react Things developed should also be subject to viewModel control .

But my personal understanding is , Controlled and uncontrolled are standing in component state ( value ) In terms of storage location , Or is it based on whether the component is called Dummy Compnent, Essentially, controlled and uncontrolled expression are the same , In a way, they can achieve each other .

3 React Community form solutions

The official plan is simple and intuitive , But it's a bit crude to write the requirements directly , The scene is a little complicated, but the efficiency is not very high . So naturally ,React The community has provided quite a number of three-party form solutions , Now I will talk about several typical ones . It should be noted that , Because many design forms are interoperable ( Learn from each other ) Of , So some functions ( For example, support for nested data ) The specification of / I'll just pick one form to say .

3.0 Pre concept

Before going deep into the various form schemes , I would like to add a pre concept , namely Field. that Field What is it? ?

Field It's a broad concept , It can be simply understood as a form component ( For example, input box ) Abstract of . We need to discuss this issue more specifically , We can start from the beginning , How are we going to be in React Write a form in it ?

  1. From a controlled point of view , First of all, we will define a state To manage the state of the form , Then we will mount each form component with value+onChange.

  1. Next, we may want to add the definition of validation rules for form items , Add the title , Statement name, Complete the mapping between the form item and the underlying data structure , In fact, that is Field The role of . therefore Field It mainly helps us solve the state binding of specific form components 、 Verify binding , And other things like label Settings and even styles , Error information display and a series of common logic .

  2. Familiar to us antdesign In the form Form.Item In fact, it can be understood as a Field.

You'll see in the back , Almost all class libraries contain this concept . Essentially , You can think so , every last Field Encapsulates all the information related to the input box it contains .

3.1 rc-form (Antd Form 3.x )

3.1.1 background

This form is actually a familiar component library Antd Design Form 3.x The bottom layer of dependence , I've used it in essence Antd Design Form Most of the students can think that they have used this form . The decision to start with this form is also based on this .

Of course, the form itself is not complicated , Distinctive features , Whether it's source code or exposed API They all have a sense of age , You can see at a glance that class component The product of the times .

3.1.2 Example

Take a brief look at the official example , Feel under :

import { Form, Icon, Input, Button } from 'antd';
function hasErrors(fieldsError) {
return Object.keys(fieldsError).some(field => fieldsError[field]);
}
class HorizontalLoginForm extends React.Component {
componentDidMount() {
// To disable submit button at the beginning.
this.props.form.validateFields();
}
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { getFieldDecorator, getFieldsError, getFieldError, isFieldTouched } = this.props.form;
// Only show error after a field is touched.
const usernameError = isFieldTouched('username') && getFieldError('username');
const passwordError = isFieldTouched('password') && getFieldError('password');
return (
<Form layout="inline" onSubmit={this.handleSubmit}>
<Form.Item validateStatus={usernameError ? 'error' : ''} help={usernameError || ''}>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>,
)}
</Form.Item>
<Form.Item validateStatus={passwordError ? 'error' : ''} help={passwordError || ''}>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Password"
/>,
)}
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" disabled={hasErrors(getFieldsError())}>
Log in
</Button>
</Form.Item>
</Form>
);
}
}
const WrappedHorizontalLoginForm = Form.create({ name: 'horizontal_login' })(HorizontalLoginForm);
ReactDOM.render(<WrappedHorizontalLoginForm />, mountNode);
 Copy code 

The corresponding page is shown in the figure below :

3.1.3 Test run source code

Simply clone the source code ,nvm Switch to 10.x Next ,yarn then yarn start that will do , The project itself comes with dev server, Then try to modify the source code to take effect immediately .

In the process of reading the source code , There are some little things that are not commonly used , Hope to help you read the source code :

Mixin

react Early offers based on createReactClass Logical reuse means of , But official documents say there are a lot of problems , stay ES6 class I don't even support it , The purpose is to reuse some general methods independent of specific component classes .

hoist-non-react-statics

To copy React Class Component Static methods in . The application scenario is Hoc When it comes to packing , It's hard to know which methods are React Provided , Which are user-defined , And this method can easily put the non - components of the wrapped components into the React Copy static methods to exposed HOC above ( For details, see links. ), In this way, we can use HOC Static methods on wrapped component classes are not lost .

3.1.4 General train of thought

This is a picture that I left when I read the source code , I hope that's helpful .

3.1.5 Integral design

actually , Feeling rc-form The overall design is relatively simple , Starting from the official controlled components ,getDecorator It's actually a HOC, For internal use React#cloneElement take value & onChange Injected into the form control . In this way , All form controls are rc-form Took over , In the subsequent interaction process, whether it's input or output , The error status information after verification is hosted inside the component , Users are really liberated .

There is a fly in the ointment ,rc-form Used a forceUpdate To complete the internal state and view UI Synchronization of , This forceUpdate It's on Form In the component , let me put it another way , Any small change ( For example, the input of a field ) Will result in rendering at the whole form level ( We used to call it global rendering ), That's what happened later rc-form It is widely criticized as the root cause of performance problems , Its essence lies in its rough rendering granularity .

3.2 rc-field-form (Antd Form 4.x)

3.2.1 background

Based on some of the defects mentioned above ,Antd 4.x The form module has been redesigned . More specifically , Design ideas , The control of rendering granularity is materialized from the form level to the component level , Make the form performance greatly improved . A lot of use in source code React Hooks, At the same time, it simplifies the exposure of API, Improved ease of use .

3.2.2 Example

Can be compared with the above code examples , The most intuitive change is to cut off the so-called getFieldDecorator On the whole, I feel really refreshing .

import { Form, Input, Button, Checkbox } from 'antd';
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
const tailLayout = {
wrapperCol: { offset: 8, span: 16 },
};
const Demo = () => {
const onFinish = values => {
console.log('Success:', values);
};
const onFinishFailed = errorInfo => {
console.log('Failed:', errorInfo);
};
return (
<Form
{...layout}
name="basic"
initialValues={{ remember: true }}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
label="Username"
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input />
</Form.Item>
<Form.Item
label="Password"
name="password"
rules={[{ required: true, message: 'Please input your password!' }]}
>
<Input.Password />
</Form.Item>
<Form.Item {...tailLayout} name="remember" valuePropName="checked">
<Checkbox>Remember me</Checkbox>
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, mountNode);
 Copy code 

3.2.3 Test run source code

Simply clone the source code ,yarn then yarn start Running , It reminds me that I missed hast-util-is-element, After installation yarn start Successfully started dev server, Then try to modify the source code, and the discovery will take effect immediately . This is a general flow chart I compiled at that time :

  1. The overall code is relatively clear and readable , In fact, the idea here is very simple , We know it's mandatory rerender Essentially, it's going to be the latest props / state Retransmit down the component tree , Its essence can also be understood as a way of communication . It's just that the cost of this communication method is that all components will be re rendered .

  2. Since the Form On the brain rerender Will cause performance problems , Then the direction of solution must be as small as possible rerender The scope of the , How to update the form status just Synchronize to the form items that need to be synchronized ? naturally , The subscription mode is used here .

  3. Based on the above design , Unfolding is actually 2 spot :

  • Each form item maintains its own state change ,value-onChange In fact, it only circulates on the current form item ;
  • Inform other form items after circulation , At this time, how do other form items know that they care about the currently changing form items ? here field-form Introduced dependencies,shouldUpdate To help users easily declare the form items they depend on .

3.2.4 Support nested data structures

Actually in rc-form I missed a more important part of the article , That's the support for nested data structures , For example, we know that actually users fill in the form and end up with a big json. For relatively simple scenes , Maybe the form is json Only one level .

For example, in the login scenario , The more common structure :

{
phoneNumber: '110', // mobile phone 
captcha: 'YxZd' , // Picture check code 
verificationCode: '2471', // SMS verification code 
}
 Copy code 

But in fact, in complex scenes , It's very likely that there will be :

{
companyName: 'ALBB',
location: 'London',
business: {
commerce: {
income: {
curYear: 110,
lastYear: 90,
}
},
data: {
dau: 1,
}
},
staffList: [
{
name: 'zhang3',
age: '22'
},
{
name: 'li3',
age: '24'
},
]
}
 Copy code 

Now , ordinary <FormItem name="field1"<Input /><FormItem> Maybe not enough , For example, we need form items to be able to form nested structures of objects and lists , We need to represent what we call nested relationships at the form level . Generally speaking , Forms support nested data through the unique identification of form items name On , Here is to field-form For example :

import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Form, Input, InputNumber, Button } from "antd";
const Demo = () => {
const onFinish = (values) => {
console.log(values);
};
return (
<Form name="nest-messages" onFinish={onFinish}>
<Form.Item
name={["user", "name"]}
label="Name"
rules={[{ required: true }]}
>
<Input />
</Form.Item>
<Form.Item
name={["user", "email"]}
label="Email"
rules={[{ type: "email" }]}
>
<Input />
</Form.Item>
<Form.Item
name={["user", "age"]}
label="Age"
rules={[{ type: "number", min: 0, max: 99 }]}
>
<InputNumber />
</Form.Item>
<Form.Item name={["list", 0]} label="address1">
<Input />
</Form.Item>
<Form.Item name={["list", 1]} label="address2">
<Input.TextArea />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById("container"));
 Copy code 

You can see that the essence is to flatten and store the nested information of data fields in name in ( here name It's an array ), Of course, a more common approach is to take the form of lodash get/set Allied path The rules :user.name user.age address[0], It's just different in performance , It's essentially the same . About why antd 4.x Use arrays instead of the more common approach , The officials also gave an explanation :

In rc-form, we support like user.name to be a name and convert value to { user: { name: 'Bamboo' } }. This makes '.' always be the route of variable, this makes developer have to do additional work if name is real contains a point like app.config.start to be app_config_start and parse back to point when submit.

Field Form will only trade ['user', 'name'] to be { user: { name: 'Bamboo' } }, and user.name to be { ['user.name']: 'Bamboo' }.

As you can understand , It's actually done inside the form , So the fields , Regardless of level , It's all managed by the camera , Although it's essentially a tree , But the information about structure is only in name. For example, we print out the above example, which is essentially :

In fact, I understand this , Let's go back Antd 4.x in form A crowd of API Medium NamePath It's not hard to understand :

All operations on form items are accepted NamePath And then operate on the corresponding match , In another dimension , In essence, what you bring in is NamePath It's the path to operate the node . Of course, a little bit of attention here is when the target node of the operation is a non leaf node , Updates need to be synchronized to all of its descendants .

It's not complicated to implement this piece , Want to know the students can turn over the source code , have a look valueUtil.ts,Field.ts#onStoreChange that will do , No more details here .

3.2.5 Add and subtract arrays dynamically

Now with nested data support , Let's look at another question , Most of the time, we don't know in advance how many items there are in an array in the form , For example, when the user enters his hobby again , He can have as many hobbies as he wants , At this time, we need to use the dynamic increase and decrease array .

although antd 4.x Provides Form.List Component to help us easily build a form that dynamically adds or subtracts arrays , But the implementation can't get around the nested data structure we mentioned earlier . Let's start with an official example :

import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Form, Input, Button, Space } from "antd";
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
const Demo = () => {
return (
<Form>
<Form.List name="sights">
{(fields, { add, remove }) => (
<>
{fields.map((field) => (
<Space key={field.key} align="baseline">
<Form.Item
{...field}
label="Price"
name={[field.name, "price"]}
fieldKey={[field.fieldKey, "price"]}
rules={[{ required: true, message: "Missing price" }]}
>
<Input />
</Form.Item>
<MinusCircleOutlined onClick={() => remove(field.name)} />
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => add()}
block
icon={<PlusOutlined />}
>
Add sights
</Button>
</Form.Item>
</>
)}
</Form.List>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById("container"));
 Copy code 

The actual rendered page looks like this :

The source code for this piece is very simple. I won't repeat it , If you are interested, you can have a look at rc-field-form#src#List.tsx, Essentially, all it has to do is 3 individual :

  1. Just to maintain an array that can be increased or decreased for users , Each object in the array corresponds to a node in the form array ;
  2. When users want to add item when , Create a new... In the array field, And give a unique key Simultaneous basis item Position generation of name, At the same time call onChange To inform the form object to update its internal state ;
  3. Deletion and addition are the same .

problem : So in antd Each of the above field Three properties on the object : fieldKey,key,name What are they all for ?

among name It's better to understand , It corresponds to the position of each input box in the final submitted data structure ,key Represents the unique id ( We can do react loop Medium key), What has been puzzling to me is fieldKey, Because in fact field-form In the source code field It doesn't contain the value ,antd This value is not specifically explained in the documentation . But it turns out fieldKey and key It's essentially the same thing , Because in antd#components#Form#FormList And I found out that :

3.2.6 feeling

In fact, just contrast Antd 3.x as well as Antd 4.x The form behind it , We have come to an interesting conclusion : The essence of the performance problem of the form scheme is Solve the communication problem between each form item and the whole form . here antd 4.x Using subscription selective notification instead of mindless rerender Stream redrawing , In essence, it is more fine-grained component communication, which can achieve less communication cost . Next, we will look at the development of several similar forms abroad , It also confirms this view .

3.3 ------- International dividing line --------

In fact, domestic communities are mainly based on antd form Mainly , But actually , Abroad React The form scheme of the community has a similar development idea . Let's go through it , Similar ideas will not be discussed in detail .

3.4 Redux-Form

Redux-form It's an earlier one react The form scheme , because redux The popularity of , Use redux It's a natural way to manage the status of forms .v6 The previous form was like this :

There are two problems :

  1. Performance issues : Any key operation of the user will trigger the update of the state, and then render the whole form globally , You can see that this problem is different from what we said before rc-form The problems are similar .
  2. rely on redux: use redux To manage the form data stream turned out to be unnecessary , All in all, it increases the volume , And it leads to a lot of things that don't need to be redux Mandatory installation of redux. You can see about this Dan That's what I'm saying .

If you're still hesitating , You can see :

Dan Opinions expressed : Good practice standards

redux The view of the official website :Should I put form state or other UI state in my store?



redux-form The author thinks that :redux-form

in fact redux-form stay v6 Then the update policy was changed , take Field Register separately to Redux, use Redux A natural subscription mechanism enables direct access to Field Update . It can be said that similar rc-field-form about rc-form The optimization of the .

3.5 Formik

3.5.1 background

Whereas Redux-form Strong dependence Redux The problems brought about by ,Formik Forget it Redux, I maintain a form state internally . I've looked at the author's design motivation , The main thing is to reduce the template code & Take care of form performance . Although the FastField To do some performance optimization , However, it is still a coarse-grained state update from the perspective of the whole form , So in essence, there is no escape from global rendering , Put aside FastField Look at , It and rc-field The design idea is even similar .

3.5.2 Example

3.5.3 The core idea

So-called FastField performance optimization , It's just through a layer of HOC The actual Field, And then in this middle layer, we use shouldComponentUpdate Determines whether the status of the current update is the HOC Wrapped Field state .

As you can see, it's just rude about several key states in the form value, error,touched And the incoming prop Length and isSubmit Several key fields for shallow comparison , If you encounter a similar field a Decision fields b Is it an input box or a drop-down box , You have to do it yourself shouldUpdate The logic of . So on the whole, it can be said that :

3.5.4 React DevTools Tips

While exploring this form , Here's an interesting conclusion , Because only FastField This outer layer connect Access context, Inner layer shouldComponentUpdate To optimize the mechanism , At this time through devtools in highlight updates You don't see if it's global rendering , That thing will mislead you in this situation , A better way to do this is to use the plug-in profiler, The following example illustrates this very well :

Demo Address :https://codesandbox.io/s/7vhsw

3.5.5 feeling

On the whole ,Formik Completed its original design , from redux Free up , And to a certain extent, it alleviates the form global rendering problem , The packing volume is also very small, only 12.7k, stay redux-form The age of rampant force is awesome ( No wonder it's officially recommended ). But because of its early release , For the follow-up react hooks Our support is not very comprehensive , And the underlying design still does not support a finer granularity of space update , Therefore, when the form expands or there are many interaction scenarios , Just by FastField I can't do what I want .

3.6 React-final-form

This is a Redux-form The author has been defending for many years Redux-form Another masterpiece after that , The author's original intention is to write a “ There is no third party to rely on 、pure JS、 Plug in ” Form for . On rendering ,final-form Also adopted and rc-field-form Similar thinking ,Form As a central subscriber , Responsible for the distribution of component events , And each Field Manage your own data , Independently subscribe to other form items you are interested in , And render independently .

Final-form I didn't look at it , I just got to know about it , Those of you who are interested can take a look final-form The author's speech : Next Generation Forms with React Final Form

3.7 The changes of the above five form schemes

In fact, you will find that history is always surprisingly similar , Because of suffering from rc-form Because of the puzzle of global rendering, subscription based rc-field-form, The development of foreign countries is similar : redux-form(v5) => redux-form(v6),formik => react-final-form. Sum up , The whole change can be understood in this way :

3.8 React-hook-form

3.8.1 background

Several forms have been mentioned above , We can find that these forms are maintained through a state To handle the state of the form , Whether it's centralized updates , Or distributed update by subscription , They are all based on this form state , This is it. react Controlled form mode of .

Now? , Let's change our mind , From an uncontrolled point of view , Uncontrolled forms are not used state That's it , It's through ref To get the form component directly , So you can get the value of the form directly , There is no need to maintain the value of the form , This allows uncontrolled forms to reduce a lot of unnecessary rendering .

But there are also problems with uncontrolled forms , In dynamic verification 、 Dynamic modification ( linkage ) It's not very convenient , So in react hooks After the emergence, a form library based on uncontrolled thought came into being react-hook-form, The design idea of this form is very novel , It's all about embracing the original , Embrace the uncontrolled . The core idea is that each component maintains its own ref, When checking 、 Submit, etc , Through these ref To get the value of the form item .

3.8.2 A simple example

3.8.3 The core idea

Source code Commit:1441a0186b8eab5dccb8d85fddb129d6938b994e

Demo Address : codesandbox.io/s/happy-mcc…

There are actually some problems , Because of all rerender All are field Level rather than form Grade ( Form rendering ), namely :

  1. Performance optimization : Because the status of each field is managed by the component itself , No data reflow is required , Except for this big piece , There's also a lot of nice details in the code :

    a. Shallow comparison of errors , For example, the last round of rendering showed the error message a, If the error message does not change in this round of rendering , Do not re render .

    b. The internal state of the form (isDirty,touched,submitCount,isSubmitting wait ) I used to Proxy packing , Use... In the first rendering Proxy Record the user's subscription to each status , If you don't subscribe, changes will be ignored , No re rendering is triggered .

    c. although watch Global rendering is triggered by default , however useWatch You can notify the update of a field without triggering global rendering , It's essentially a subscription mechanism , take useWatch Caller's state hook Maintained on the internal object of the form , Once there is an update, you can only notify the subscription component in this way .

  2. It is a pity , Error information changes under any form , Will trigger global rendering , This doesn't feel particularly good . At least ErrorMessage There can be a shallow comparison .

3.8.4 Dynamic verification and linkage

To support dynamic verification ,react-hook-form In the process of form registration, the onChange、onBlur And so on , Make sure to input with the user 、 Monitoring of modification behavior , So you can check the form 、 Form value monitoring, etc .

The problem of dynamic verification of uncontrolled forms , There is also the problem of linkage . because react-hook-form The value of the form will not be maintained in state in , User input does not trigger the entire surface JSX to update , therefore react-hook-form Provides watch, And better performance useWatch, To register the forms that need to be linked , Updates are called when the user makes changes .( In essence, these two things and rc-field-form Medium dependences / shouldUpdate The purpose is similar ).

About watch and useWatch

In fact, there is a performance difference between the two ,useWatch You can think of it as moving the update to a more local location , So there will be more advantages in performance :

3.8.5 Compatible with three parties UI library

Because most third parties UI Ku is following react Designed by controlled thought , for example Antd#Input There's no ref, The government also provided Controller Components , The third-party components packaged with it only need to follow the default controlled specifications onChange / value that will do ,Controller Will build internal state in it , In disguised form, it simulates Uncontrolled Component.

3.8.6 feeling

In the end react-hook-form In fact, with other controlled form library can be said to be the same goal , It's all about state distribution management , The update of a single form item does not affect other form items , And linkage can be said to use the subscription to do , To some extent , Based on uncontrolled implementation react-hook-form It's even more natural to see it all . You can see , Uncontrolled can also do whatever controlled forms can do , That's why I'm personally 2.3 It's mentioned in the section , Controlled and uncontrolled can realize each other in a way .

3.9 Transverse comparison

Here's a horizontal comparison chart :

4. Formily (1.x)

4.1 background

If you don't know, you haven't known before Formily Maybe the following content will be a bit abrupt , I suggest you have a general understanding of . Because I'm not going to talk about Formily The basic usage of , Just to talk about my understanding of Formily Some understanding of .

The reason why Formily Take it out as a lecture , Because at this stage of my form research work (2020.10), It's no exaggeration to say ,Formily It was within my cognitive range at that time , The most advanced design concept ( It can also be said to be radical ), One of the most thoroughly researched forms solutions in the field of forms . When I write the summary , It's a completely new version 2.x It's already made preview edition , It is said that there are many improvements , But I haven't had time to look at it , So the following is only for 1.x.

4.2 scene

In my mind , although Formily The original intention is to be large and complete , The consideration of scene preset is relatively complete , But I think it's more suitable for high complexity , There are many forms , Form scenarios with extreme performance requirements , If it's just a simple scene, it's really unnecessary , As we said ,antd 4.x I've asked for full rendering , If we can make rational use of dependencies / shouldUpdate In fact, the performance is good enough . But if your scene is particularly complex , In particular, there is a lot of linkage logic , utilize formily Or can effectively help you to converge logic , To reduce the mental burden .

4.3 Learning cost

Although I said above Formily The concept is advanced , But in fact, the industry has different opinions on it , The most criticized problem is the cost of learning . I personally think it's a hindrance Formily The core issue of the fire ( When I write articles Formily There has been a 3000 Multiple star 了 ). Frankly speaking , Formily Compared with other forms in the community, the cost of learning is still on the high side , In fact, there are two main reasons :

  1. User documentation :

    a. stay Formily You can often see user feedback in user groups Formily Slow access to official documents , I can't even open it , This is a point I often encounter .

    b. User documentation is not clear enough , I don't think it's clear in many places , Many details are not mentioned , You need to find out for yourself , This point antd design It's a model in the industry . ( This is not absolutely , After all Formily It can be regarded as a high-level class library in the form vertical field ,antd With component library as the core , There is also a gap between the two )

  2. The whole plan is big and comprehensive , The concept is advanced , This also leads to more new concepts , Like schema Descriptive structure ,coolpath Path system ,effects Even introduce rxjs To do Linkage Management , Although we will find that there is a certain truth in the introduction of all concepts , But for most developers , The lowest possible cost of learning , As high as possible development efficiency is what they pursue , therefore Formily A lot of sophisticated concepts are very unfriendly to some new users .

4.4 idea

It says Formily The cost of learning is so high , But I really want to have a chat , I even had a separate session to talk about Formily. Because its design concept really represents the relatively advanced level of the form industry . Let's talk about some of my own impressive points :

4.4.1 Communications

4.4.1.1 From data backflow to subscription to effects

Look at the industry forms one by one , I have a very intuitive feeling of myself , In the final analysis, each form scheme , In fact, it is to solve the communication problem between various form items and between form items and the whole form :

  1. We can see the beginning redux form (< 6), rc-form, They go through the full amount of rerender After that new props It's coming through the layers, informing everyone Field On , In this way, the efficiency of information communication is very low , The performance of the form is poor .

  2. Back rc-field-form And other forms are updated independently , Relying on projects to subscribe to updates , This essentially brings better communication efficiency and better form performance .

  3. Besides , Without exception, they all admire onChange + fomrRef To express logic :

naturally , Formily It's also subscription type , But it's more unique in its performance , Because it converges all of your form related logic to one called effects In the field of , The writing is very novel , namely :

The core here is $('event_type', 'path_rule'), It means to search all path_rule Hit form item , Subscribe to their event_type event , Finally, the overall return is a rxjs flow . Here I would like to make a few points :

  1. First use effects Convergence of various form behaviors , Linkage logic this is really a great initiative , Compare with other solutions hanging on components onChange Callbacks are scattered throughout the view layer , This convergence really helps improve the readability of the code .

  2. path_rule We must follow the set of the author's own implementation DSL, namely cool-path, It feels like the starting point is good , The purpose is to accurately locate a specific form item , Also have the ability to do batch processing according to the situation , Bulk subscription . This is actually a big innovation , It gives Formily Strong ability of linkage expression , one-on-one , One to many , Many to many can be well expressed . But the bad thing is that the documentation is incomplete , But its grammar is a little different , It's neither a path system nor glob pattern, In fact, to be honest, I've used it for a long time, and sometimes I still can't understand it .

  3. Introduce here rxjs, It's really geek, But as far as I'm concerned, I feel that all the scenes are just used as simple event subscription .

4.4.1.2 About react-eva

In fact, this writing method is not groundless ,effects + actions + rxjs This combination is also created by the author , For this purpose, a library is abstracted , It's called react-eva, The idea is clear , Around rxjs in the light of react Component is an internal and external communication scheme :

This program has 2 A core advantage :

  1. Good linkage performance , It's natural to follow this specification without having to use it in the view layer hooks 了 .
  2. Improved code maintainability , All the logic converges to effects in .

4.4.1.3 from “Effect Only once ” On the core understanding

A colleague asked me this question before : Why is it clear that one of my react hook It's been updated, but in the latest effect One of the observer Medium or old values ?

In fact, it will be found after a few more tests ,effects The function itself is executed only once . It's a superficial reason , But I understand the deeper reason is , The author doesn't want to let effects( It can be thought of as the linkage logic of your statement ) and Of the current view layer hooks Depend on each other , There are two reasons :

  1. effects It should be something pure , similar reducer, It can even be detached and reused .
  2. On the other hand , If effects and View layer hooks Coupled , It means that every time you need effects If we do it again, we need setState, Before I know it, it's the redrawing of the whole table , This can be seen as a performance regression , Obviously the author doesn't want to see .

So from my perspective Formily It's not that React, It's more like an automaton , You wrote it. JSX / Schema Just declaring the structure of the form , Once the first rendering is complete , This form is completely accessible through User interaction + Developer defined effects Complete the form . Its life cycle is more like its own independent rather than belonging to the package React Component containers . This is in “Formily The data model for is the form UI The complete expression of ” It's also confirmed in the book .

4.4.2 The underlying data model

4.4.2.1 Subscribing model as base class

I just said that all the events in the form can be accessed through effects Declare and monitor , in fact , This is still true inside the form . You can say that , Whole Formily Inside and outside , It's all based on a simple idea , All structures can be subscribed to , For example, his class inheritance model is as follows :

4.4.2.2 Forms = A bunch of fields organized in a tree structure

Another thing that impresses me is ,Formily The underlying data model is a complete representation of its entire form . When looking at other form schemes , Although you can also see the inside of each form in the form of store,state The state of , What's in it is nothing more than valueinitialValue,rules,dirty,touched And so on, some status related information . And then to Formily:

Formily Not only that , It even passes in the input component of a field props, The mount status of the current form , The display status has been described . There are several points reflected here :

  1. The state of the whole form can fully express the UI, It means that we are effects You get a high degree of freedom when working with forms in , You can even set a field by state To control the drop-down list of this field , Simply modify the data to update the view , Very pure . For other forms , You probably need to be in JSX Write a ternary expression or have a useState Of hooks It's used to manipulate view layers .

  2. The complete expression of the state here means ,Formily Jump out of React The confinement of view frame , It's a complete kernel , You can drive any view frame completely through it , for example Vue. Other forms actually have more or less information of view layer left behind React Of UI On the component .

in fact , It's not just concrete field, If you raise your vision , You will find that the whole form can be fully expressed in a tree structure :

Think carefully , It's quite natural , Because in the case of nested data , The natural structure of a form is a tree . so to speak , This tree is the complete expression of the form , Combine the previous effects, All interaction of the form can be done in effects Internal closed loop , because Formily The data layer of can do UI The complete expression of the layer .

Back to us 4.4.1.3 The question in the section , You can see , Actually Formily It should be very Redux Of :

It doesn't have to rely on any React dependent hooks/callback Can achieve their own link closed loop , I've probably cut the whole link :

4.4.2.3 Immer

however , Just now effects Of state You can manipulate forms very freely / Any property of a form item . It brings a lot of freedom , It really improves the user experience of the library , But the workload doesn't disappear out of thin air , about Formily For developers of , Users just set up , Then they need to get to the bottom of every situation , For example, users are simply effects Pass through state Reset rules, that Formily The bottom layer has to consider re checking , Form error status update and so on . How can the author know which attribute we modified in the callback ?

The answer is Immer. The first time I saw it was really big Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo Hoo , Because in my shallow cognition ,Immer The value of being is just “ Write redux reducer It's easy to create immutable objects ”, But the author actually thought of using immer Medium patches To record the user's operation record , It's very natural to use it here , The whole idea is as follows :

It's really wonderful here , Because it involves comparing the changes of two objects before and after the function , The first thought is “ Dirty examination ”, The author skilfully bypasses the dirty check . But my question at the time was : Used in immer Is the performance overhead really going to be smaller ?

For this reason, I specially studied immer Principle . Simply speaking Immer Our high performance is based on the fact that :

newly build / The overhead of copying objects is high , The overhead of references is low .

So for better performance, we should create as few new objects as possible , In this context ,immer Realized Copy on write The effect of :

Of course , For small forms, performance optimization is optional , But you can see that Formily There is an extreme squeeze on performance .

4.4.3 Schema And Low / No Code

4.4.3.1 Concept

Formily There's another one on the top called Schema The concept of , Why would there be Schema Well ? In order to better explain Schema, Let's talk about Low / No Code.

Form oriented Low / No Code, Generally speaking, it is the visual construction of forms , Product form is all kinds of form generator . But for the Low / No Code,Wiki There's a more accurate definition of :

A low-code development platform (LCDP) is software that provides a development environment used to create application software through graphical user interfaces and configuration instead of traditional hand-coded computer programming.

No-code development platform (NCDPs) allows programmers and non-programmers to create application software through graphical user interfaces and configuration instead of traditional computer programming.

Its advantages are obvious , That is to lower the development threshold , The work of software development is no longer limited to professional technicians . The industry's so-called front-end enabling , One form is construction No / Low Code platform , Leave the requirements to the non front end .

4.4.3.2 Product and Implementation

Include Formily , , There are already some form generators on the market , for example :

Basically all the forms No / Low Code programme , We can't do without a core concept , namely DSL.DSL Think of it as UI The bridge between view and user interaction . Generally speaking, the process of this kind of product is like this :

  1. In some non coding ways, the user ( For example, drag and drop configuration on the platform ) production DSL;
  2. DSL Convert to view by mapping rules .

therefore DSL It's the media , It's actually a code representation of an abstract model , for example :

Users may not know what is select,input But he knows what a drop-down box is, an input box ;

Users can not know what is relative positioning, absolute positioning , But he knew he had to let the box go a little further to the left ;

Users may not know what is required But he will tell you that I want this item in the form to be required .

Here is Formily By expanding JSON Schema Defined Schema, It can also be regarded as a kind of DSL:

about Formily for , It exists at the same time 3 It's an equivalent expression , see :https://formilyjs.org/#/0yTeT0/8MsesjHa, Closest to configuration JSON Schema, And then there was JSX Schema, The last is the closest to React Pure JSX. We've said that before , Formily Of JSX Layers are more like declaring a bunch of form structures than views , The underlying logic is here . It has to make sure that you end up presenting a consistent form using any expression .

Formily introduce Schema There is a great vision -- That is, subsequent forms can be produced by machines or configured by visualization platform . But in the react-schema-editor To see if , The degree of temporary completion is still relatively limited . In my own business, actually Schema Also contact less , To some extent , For those who don't need to consider configuration / Machine generated form scenarios , In fact, this layer has become our mental burden . My personal feeling Formily The part of grounding gas is still its React layer (core layer ) + Antd Bridge layer .

4.4.3.3 present situation

Like this, we hope to provide a class library for visualization solution ,Formily Not the first , But most of the feelings have not been highly accepted , There are several aspects of my own understanding :

  1. Most of these platforms are used to enable non front-end students , In fact, the configuration process ( That's the development process ) It is not difficult to , The hard part is how to balance product flexibility with ease of use , It's easy to fall into “ R & D doesn't want to use , Nontechnical users don't use ” The dilemma of .
  2. On the other hand , Most solutions only address the first step of business requirements , Development , This is often the simplest . But how to expand the whole link , Take into account the entire life cycle of the product , That is, the follow-up under this new form of development debug Can optimization and improvement also be achieved 0 Participate in ? I don't feel very mature right now , There's more to explore .

4.4.4 other

Actually Formily I have a little more contact with the above concepts , For other things, such as style layout, we really use less in our business , If you don't know much about it, you won't be mistaken . Although it means Formily The user documentation for is not very good , however Formily The conceptual introduction article is very well written , If you want to Formily If you have a deep understanding , Here's the official team Formily Know about columns .

about Formily, To be fair , They're sort of explorers in the field of forms , It's not perfect , There are such and such problems , But there are no hidden faults , I can see the great wisdom of Chinese developers , Also gave me a lot of inspiration . For those who want to know more about forms , I personally think Formily It's really a good learning object .

5. ending

Some ideas about form technology have been scattered in every corner of the article , When it comes to the conclusion, I feel that there is nothing to say . Hurried writing , In addition, the article is subjective as a whole , Some arguments may not be in place , If there is something wrong , Welcome to exchange and correct at any time .

6. Hard ads for recruitment

Urgent recruitment ! We are the front-end team of the game , The current business of the team includes several DAU Tens of millions of game centric platforms , Cover today's headlines 、 Tiktok 、 Watermelon video and so on, many bytes jump on the host side ; There is also a ten million level creator service platform , Tiktok is the most important official game of short video distribution platform and master's cash flow channel . The team is responsible for these project products , And the related operation background 、 Advertiser service platform 、 Front end development of efficiency tools , Business technology includes small programs 、H5、Node Wait for multiple directions , And with the rapid development of business , Continue to enrich technical support scenarios .

Welcome to click Portal , Or direct email :lupengyu@bytedance.com.


Welcome to your attention 「 Byte front end ByteFE 」

Resume delivery contact email 「 tech@bytedance.com

版权声明
本文为[front end]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/02/20210223024103671w.html

  1. JavaScript advanced: Javascript object-oriented, JavaScript built-in object, JavaScript BOM, JavaScript encapsulation
  2. JavaScript advanced: Javascript object-oriented, JavaScript built-in object, JavaScript BOM, JavaScript encapsulation
  3. Vue determines whether the El form in the elementui is updated or changed. If it changes, it will prompt whether to save it. If it does not change, it will leave directly
  4. Algorithm problem: sum of two numbers -- JavaScript and Java implementation
  5. High performance nginx HTTPS tuning
  6. JQuery advanced
  7. day 30 jQuery
  8. JQuery:JQuery Basic syntax, jQuery selector, jQuery DOM, comprehensive case check box, comprehensive case random picture
  9. TCP/IP 开胃菜 之 HTTP
  10. JQuery:JQuery Basic syntax, jQuery selector, jQuery DOM, comprehensive case check box, comprehensive case random picture
  11. JavaScript data type
  12. [micro front end] the final chapter of micro front end - Qiankun guide and overall exploration of micro front end
  13. Solve Ajax cross domain problem [5 solutions]
  14. HTTP of TCP / IP appetizer
  15. Optimization of pod creation efficiency in serverless scenario
  16. Iqiyi Sports: experience the ultimate expansion and contraction of serverless, and increase the utilization rate of resources by 40%
  17. First knowledge of HTTP / 1.1
  18. First knowledge of HTTP / 1.1
  19. Webpack learning notes series 05 devserver
  20. Webpack learning notes series 04 - resource processing optimization
  21. How to build a high performance front end intelligent reasoning engine
  22. How to become a professional front end engineer in 2021?
  23. How to transform single / micro service application into serverless application
  24. How to transform single / micro service application into serverless application
  25. How to transform single / micro service application into serverless application
  26. How to connect the ground gas to the micro front end?
  27. How to connect the ground gas to the micro front end?
  28. How to connect the ground gas to the micro front end?
  29. Vue server rendering principle analysis and introduction
  30. Realize the correct loading of text message
  31. Building my own project scaffolding with yeoman
  32. JavaScript advanced prototype and prototype chain
  33. React background management front end system (based on open source framework development) start
  34. JS practical skills breakpoint debugging
  35. I'd like to share with you 20 super easy-to-use Chrome extension plug-ins
  36. Get page element location
  37. Use the powerful API of modern browser to record any interface in the browser and realize export, save and management
  38. Delayed code execution in flutter
  39. Reconfiguration experience of KOA middleware system
  40. Add comments to your blog
  41. Svg editor -- new path
  42. Detailed explanation of debounce and throttle of JavaScript function
  43. Anti shake and throttling and corresponding react hooks package
  44. C2m: the first CSDN article moved to MOOC script 5000 words, detailed painstaking development process, there are renderings and source code at the end of the article
  45. Front end, school recruitment, Taobao, guide
  46. [vue2 & G6] get started quickly
  47. Canvas from the beginning to the pig
  48. Take five minutes to standardize the code comments?
  49. Some thoughts on sass
  50. what?! You haven't filled in the award information yet
  51. How to get the interface + tsdoc needed by TS through swagger
  52. Binary tree
  53. Canvas drawing method in Web screenshot
  54. Front end docker image volume optimization (node + nginx / node + multi-stage construction)
  55. Become a big influence of technology? Coding pages quickly build personal blog
  56. Object and array deconstruction, spread operator, rest operator
  57. Analysis of Axios source code
  58. Two ways to delete useless code in project (Practical)
  59. Edit your picture with canvas
  60. Today's chat: 2-4 years to walk out of the resignation dilemma and comfort zone