Using concent, experience a journey of progressively refactoring react applications

In the traditional redux project, the state we write in the reducer is bound to get through to the store. We need to plan the definitions of state and reducer at the beginning. Is there any way to enjoy the benefits of the separation of ui and logic quickly without starting from the rules and regulations according to the text? This article starts with the ordinary react writing. When you receive a demand, you have a general definition of the interface of components in your mind. Then you can slip into the concent world and feel the gradual pleasure and the unique charm of the new api.

Demand has come

Last week, the weather was not very good. I remember several rains. But the sound insulation of Beijing Headquarters Building was so good that I didn't feel the wind and rain outside. When I was thinking about sorting out the existing codes, I received an ordinary demand, which was about to realize a bullet window.

There is an optional list of fields on the left. Click on any field and you will go to the right.

On the right side, there is a list of selected fields, which can be dragged up and down to determine the order of the fields in the table to determine the display order of the column fields, and can also be deleted to restore it to the optional list.

Click Save to store the user's field configuration to the back end. The next time the user uses the view form again, the user uses the configured display field to display.

This is a very common requirement. I believe that many code gods have finished reading the code prototype in their minds. Hey hey, but please read this article patiently and have a look at it. concent With the help of our slogan, how will your react application become more flexible and wonderful?

concent, power your react

Preparation

The product students expect to see the general effect prototype quickly, and I hope that the prototype can be the basic code of continuous reconstruction and iteration. Of course, we should take it seriously. We can't scribble a version for the purpose of intersection, so we need to quickly sort out the requirements and start preparing work.

Because a lot of projects write UI based on antd, after listening to the requirements, a shuttle box-like component pops up in my mind, but because the right side is a draggable list, consulting the following no similar components, then realize one by yourself, preliminary sorting out the following ideas.

Component named ColumnConfModal, And-based Modal, Card-based layout, And List to achieve the left selection list, react-beautiful-dnd based drag-and-drop api to achieve the right drag-and-drop list.

Because this pop-up window component is used by different tables on different pages, the incoming column definition data is different, so we use the way of events to trigger the pop-up window to open and pass the form id, open the pop-up window to get all the field definitions of the form, and the selected field data of the user for the cousin. Initialization of table metadata converges within Column Conf Modal.

Based on the interaction between the left and right sides of the table, roughly define the internal interface

UI Implementation

Because registered as a concent component has the inherent ability to emit & on, and does not need to manually off, concent automatically helps you remove its event listener before the instance is destroyed, so we can easily listen to the openColumnConf event after the registration is completed.

First, we abandon all kinds of store and reducer definitions, quickly build a prototype based on class es, and register common components as concent components using register interface. Pseudo-code is as follows

It can be found that there is no difference between the inner of this class and the traditional react class. The only difference is that concent injects a context object ctx into each instance to expose the new feature api that concent brings to react.

Elimination Life Cycle Function

Because event monitoring only needs to be performed once, in our example we completed event openColumnConf listening registration in Compoonent DidMount.

Obviously, according to the requirements, we also need to write business logic here to obtain table column definition metadata and user's personalized column definition data.

All concent instances can define setup hook functions, which are called only once before the initial rendering.

Now let's replace this life cycle with setup

//setup defined in class with a $$prefix
$$setup(ctx){
//Here we define on listeners to actually listen on events after the component is mounted
ctx.on('openColumnConf', () => {
this.setState({ visible: true });
});
//The tag dependency list is an empty array, which is performed only once in the component's initial rendering
//Simulate componentDidMount
ctx.effect(()=>{
//service call balabala.....
}, []);
}

If you are familiar with hook, do you see that the effect API grammar in setup is somewhat similar to useEffect?

The execution time of effect and useEffect is the same, that is, after each component is rendered, the effect only needs to be invoked once in setup, which is equivalent to static and has more performance improvement space. Suppose we add a requirement, every time vibible becomes false, we report back-end operation log, which can be written as

//Fill in the name of the key in the dependency list to indicate that side effects are triggered when the value of the key changes
ctx.effect( ctx=>{
if(!ctx.state.visible){
//The latest visible is now false.
}
}, ['visible']);

So far as effect is concerned, there's too much to talk about. Let's go back to the components of this article.

Upgrade status to store

We hope that the state changes of components can be recorded to facilitate the observation of data changes. so, first we define a sub-module of store called ColumnConf.

You may have noticed that if such violent annotations were removed, would the code in render go wrong? Rest assured, no, concent component state and store are naturally connected, the same setState is also connected to store, let's install a plug-in concent-plugin-redux-devtool first.

Note that the concept-driven ui rendering principle is totally different from redux. The core logic part is not wrapped on redux. It has nothing to do with redux. It just bridges the redux-dev-tool plug-in to assist in state change recording. Don't misunderstand it, no redux, concent1. It works well, but because concent provides a perfect plug-in mechanism, why not make use of the community's existing excellent resources? It's hard to duplicate meaningless wheels ()b...

Now let's open chrome's redux plug-in to see how it works.

The above figure contains a lot of ccApi/setState, because there are still many logic not extracted from reducer, dispatch /*** style type is dispatch call, we will mention later.

In this way, the state transition is much better than window.sss, because SSS can only look at the latest state.

Now that redux-dev-tool is mentioned here, let's take a quick look at what the data submitted by concent looks like.

Five fields can be seen in the figure above. renderKey is used to improve performance. We can leave it unknown. Here we will talk about the other four. Module represents the name of the module to which the modified data belongs, committedState represents the status of submission, sharedState represents the status of sharing to store, and UniqueKey represents the status of triggering data modification. Instance id.

Why distinguish committedState from sharedState? Because setState calls allow the submission of its own private key (that is, key not declared in the module), committedState is the entire state to be redistributed to the caller, while sharedState is distributed to other cc component instances that belong to the module value after synchronization to the store.

Here I borrow an illustration from the official website.

So we can declare other non-module key s in the component and get them in this.state.

Decoupling Business Logic and UI

Although the code works properly and the state is connected to the store, we find that the class has become bloated. It is fast and convenient to use setState, but the cost of later maintenance and iteration will gradually increase. Let's take the business to reduder.

Love class, love hook, let the two coexist in harmony

The react community has vigorously promoted the Hook revolution, allowing people to gradually replace class components with Hook components, but essentially Hook escaped from this and streamlined the dom rendering hierarchy, but it also brought a large number of temporary anonymous closures to be created repeatedly during the existence of components.

Let's see how concent can solve this problem. It mentioned above that setup support returns results, which will be collected in settings. Now let's change the class component bar into a Hook component by slightly adjusting the code.

Here I would like to thank Mr. Youyuxi for this article. Vue Function-based API RFC It gives me great inspiration. Now you can see that all the methods are defined in setup. When you have a lot of components, it is obvious to reduce the pressure on gc.

Since they are written in a highly consistent way, is it very natural to move from class to Hook? We don't really need to argue about who's better, just according to your personal preferences. Even if you don't like classes one day, in concent's code style, the cost of refactoring is almost zero.

Using components

We defined an on event openColumnConf above, so when we refer to the component ColumnConfModal in other pages, of course we need to trigger this event to open its pop-up window.

In the above way, if there are many other pages that need to introduce Column Conf Modal, we need to write an openColumn Conf Modal. We can abstract this open logic into modal service, which is used to open all kinds of pop windows, and avoid seeing the constant string openColumn Conf Modal in business.

epilogue

The above code is valid at any stage. To understand the online demo of incremental refactoring, you can Click here. More online sample lists Click here.

Since this topic is mainly about progressive refactoring components, other features such as sync, computed$watch, renderKey, etc., will not be explained here. Please wait for the next article.

If the inspector likes it, he will come. Stars Well, concent is dedicated to bringing new coding experiences and functional enhancements to react. Please look forward to more features and ecological periphery.