React the programming paradigm of the end

Time:2020-10-18

dvajsIt is a set of scaffolding written by Alibaba based on Elm concept for react / Redux technology stack.

Two years ago, because antd started to come into contact with the scaffold. I really need this scaffold. For beginners, it’s hard to integrate react / Redux / react Redux / react router / react router Redux – if I’m so lazy, I may not be able to understand what they are.

Of course, many high-level tools were created after careful consideration of predecessors. When I encountered difficulties in developing Mac OS / IOS, I thought of them again

  1. React initially solved the problem of single page componentization, but the state management between components has not been solved.
  2. Redux solves the problem of single page state management and provides a general solution.
  3. React / Redux is naturally combined, or they’re just looking at each other in the beginning.

One Page Application

Everything starts with a special concept OPA (one page) in the front end The advantages are obvious: you don’t have to wait for the white screen to load your page, and the switching between business and business is also smooth and natural, which has reached a profound consensus in the field of modern front-end. However, many engineering problems have not been solved in the front end: how to divide and cooperate in a large organization? How to manage the code? How to reuse components? Of course, in the view of client development, these problems are not problems at all. One activity / viewcontroller can solve all problems. If not, we will have another one. If not, we will start to nest them – we have no white screen problem.

The emergence of react component is equivalent to providing a view level namespace for the front end. Its granularity is a visual component, which contains this visual style, and also provides event response model, etc. So far, front-end development and all native client-side development (including desktop’s generalized client-side Development) are on the same starting line – finally, a component can be named, depending on the form of virtual DOM or web component. There is no problem with dom. The style independence can be solved by naming or scoped CSS. This is a small problem.

The above is the first big problem to be solved in the front-end domain. If the view component can be abstracted into a class, then the component can be shared. The development of page changes from simple HTML tag to business reuse view component, and the whole development process starts from parallel to three-dimensional.

React the programming paradigm of the end

State

View must exist. What is state? If we don’t pass in an external value to a view, some of the properties of the view will change with the generation of events. These properties are called states. These events are generated by interaction. Within a component collection, because an interaction (such as entering this page) visits the network, and the data downloaded from the network fills the view, then the data is easily part of the state.

State is not a data, it is a set of data. This is a very important concept. A set of data means that some data between two states cannot be combined with each other. For example, {A: 1, B: 2} is a legal group, and {A: 3, B: 4} is a legal group. Then {A: 1, B: 4} if it is not the combination existing in our business, theAny timeNone of our view states should have this possibility. Based on the above rules, we introduce the concept of immutable.

Immutable

Immutable is used in combination with many paradigms. The most classic example is functional programming. We know that there is no concurrency problem with pure function, because the input and output will not have any side effects on the external environment. There are two possible side effects: one is to access external resources, the other is to modify existing objects.

What if we have to modify an existing immutable object? Very simple, we use the copyonwrite strategy to go back. In this way, the input and output are still constant, and the external environment will not be affected, and the “purity” of the function is guaranteed. There are many libraries related to immutable, JS has Immutable.js Java has Google autovalue, and guava also has related implementation.

The concept of pure function provides a good direction for testability and maintainability of our code. If possible, we hope that all our functions are pure functions, just like TDD, which is our eternal goal.

So in react, the API setstate is a display of immutable. In immutable design mode, if you save a copy of the historical state, the historical state can be traced back at any time – there is such a state machine in our editor, which is very important when we do undo and redo.

React the programming paradigm of the end

Redux

In the process of using the state, we encounter a problem. Our components need to interact with other components. Generally speaking, we adopt the scheme of referring to other components to a certain extent – through callback, the more common one in MacOS / IOS is delegate. At the beginning, react can also feed data back through callback. However, if there are multiple components that need this data, we may even need to pass the callback layer by layer, as shown in the following (pseudo code)

<A callback=this.cb>
    <B callback=a.callback >
        <C callback=b.callback>
        </C>
    </B>
</A>

It may be clear that it is a business global data, which must be transmitted in this way. With viewcontroller and activity, this problem is not particularly obvious, because different viewcontrollers can be used in different scenarios, and the data of child process and parent process can be isolated by using constructor. In the front-end OPA, if different business processes need to use the same state, it is very troublesome.

At this time, we have redux. Its official website publicizes four features:

  1. Predictability: behavior consistency
  2. Centralization: state persistence
  3. Debuggable: time travel debugging
  4. Extensibility: plug in ecology

React the programming paradigm of the end

We can simply use immutable to cover the above 1 and 3 features. 2 is that Redux provides a global store to handle this (this is too simple). The state management of the store is very useful, because we can initialize the store when the server renders this page, and directly assign values to the browser’s response by using global variables. In this way, when we do server rendering, we can get the final state without state migration, which improves the efficiency of front-end rendering again.

React-Redux

After the concept of Redux was proposed, the react Redux project naturally appeared. Its role is only one, which naturally integrates Redux’s store into the ecology of react. The API provided is very simple and usefulconnect()This API is used to generate high-order component and inject store into each react component,connect()The prototype is as follows:

function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

https://react-redux.js.org/ap…

In fact, the name of this function is very simple. All four parameters are optional. Its return value is a high-order function, and the formal parameter of the high-order function is your own reactcomponent. Encapsulating the reactcomponent generates a high-order component for the business side to use. Let’s talk about the connect function briefly.

We know that Redux has a store. The store stores the global state. How to use the global state? Can a business component only care about its own part of the global state? This thing ismapStateToPropsWhat this formal parameter does, it’s a function, the prototype is like this(state) -> props

We know that each reactioncomponent has some properties (props), which are different from the state, and they are immutable. Since we have the concept of pure function, we can also have the concept of pure component, corresponding to the concept of stateless component in flutter – only props, without state, the more these components, the better.
Then, when the global state changes, we need a function to map the global state to the props of the current componentmapStateToPropsEach component only cares about its own part of the state.

In the above way, we have completed the way that a component changes the global state and thus affects the global view.

So how to produce this action? React Redux introduces a function calleddispatchThe contents of the dispatch call are action and its formal parameters. This understanding is actually very simple. We can simply understand it as a dispatch call fun.bind (xxx), then this action will have an impact on the global state, and a new state will be generated. The state will move forward. The code often seen by applications using react Redux is:

dispatch({type: “INCREMENT”, value: 1});

In essence, it calls a pure function associated with increment. This function takes formal parameters and previous state and returns a new state

function action(state, params) {
    // ....
    return { ...state, xxxx }
}

Then the returned state will be saved by the store, and all connected components will receive a notification to change their properties.

This is the logical closed loop of react Redux in the absence of network environment. We usually describe the above logical closed loop as follows:

One way data flow problem of component, action, reducer and state.

Side Effects

Once the API call is accessed, our logic becomes more complicated. Because RPC calls are basically unpredictable, even if you call idempotent interfaces, you may also be unable to access unique states in our state machine because of the network congestion

React the programming paradigm of the end

Yes, the evil API, it’s not pure. Note that this is only the case of idempotent interface, if it is not idempotent interface, the state may be more.
The first problem of destroying the pure function is that the testability of the function is destroyed. If you want to write a test case, assert() doesn’t know how to write it, because you don’t know what its return value is.

First of all, in order to solve the problem of asynchronous call (action needs to retrieve data asynchronously), there are many library options:

  • redux-thunk
  • redux-promise
  • redux-saga

As to why DVA chooses redux-saga, we can take a look at what Alipay is doing here:https://github.com/sorrycc/bl…

Redux thunk and Redux promise change the meaning of action, and action becomes less pure

They all have side effects on action. So let’s see how Redux saga solves this problem.

redux-saga

https://redux-saga.js.org/

Above is the home page of Redux saga.

Saga’s core solution is to use generator to increase our uncertainty. In the interface that needs to call API, we can get a state through branch logic call through generator. No matter what the return value of this asynchronous call is, we can get an action of issuing this asynchronous call, Saga Call it declarative effects

https://redux-saga.js.org/doc…

We can see how we can get what we just said. First, it raises a test problem.

function* fetchProducts() {
  const products = yield Api.fetch('/products')
  console.log(products)
}

const iterator = fetchProducts()
assert.deepEqual(iterator.next().value, ??) // what do we expect ?

This is the question we just asked. What is the expected value? If we want to determine this value, there are two ways:

  1. Connecting to the real server
  2. Mock data

So in testing, it’s very stupid to test with 1 (how do you test the “registration” interface? Because it is not idempotent).
So we can only use mock. Mock is actually the worst way. Mock makes our testing difficult and unreliable. If our business changes, the code of mock needs to be changed, so the workload will be greatly increased, which is very difficult.

So saga referred to itEric Elliott’s articleThe original words are:

(…)equal(), by nature answers the two most important questions every unit test must answer, but most don’t:

What is the actual output?
What is the expected output?
If you finish a test without answering those two questions, you don’t have a real unit test. You have a sloppy, half-baked test.

In translation, we need to consider what is the real output and the expected output. We can not test the API interface according to the actual results of the business. When we test the API interface, we only expect to input the correct parameters that meet the definition of us and the back-end document. Because the business return result is not determined by the front end, the decision-maker is the API provider. They should ensure that the interface document definition is met under normal network conditions through their tests.
Note that our front-end focus is on thebehaviorBecause Redux saga is based on generator, this behavior becomes easy to obtain, and our assert becomes:

import { call } from 'redux-saga/effects'
import Api from '...'

const iterator = fetchProducts()

// expects a call instruction
assert.deepEqual(
  iterator.next().value,
  call(Api.fetch, '/products'),
  "fetchProducts should yield an Effect call(Api.fetch, './products')"
)

We expect it to happen once for all/producsThis API interface call.
In this way, we can solve this problem without any mock interface. The results may be inconsistent, but the behavior must be consistent. Through the consistency of behavior, we ensure the purity of action

Input consistent parameters and output the same result (behavior).

You can take a closer look here. I’ll post the link to “declarative effects” just now

https://redux-saga.js.org/doc…

dva

So the above describes react, Redux, react Redux and react saga. In fact, DVA encapsulates the above components. Of course, I will not talk about the front-end routing such as react router. I believe everyone can understand it.

Saga and router are introduced to solve the problem of pure functions, and new problems are also born

  1. Redux project module is too scattered
  2. It’s very troublesome to create saga. You can see the document for this

This part is in theDevelopment and selection of Alipay front-end application architectureAs mentioned in it, DVA encapsulates these logic, and solves the above two problems by using declarative routing and model solutions. Let’s use the whole set of solutions more comfortable.

After understanding Redux saga, using DVA can improve the engineering efficiency.

The dilemma of client developers

Due to the lack of language level treatment (possible) and lengthy process of function first class for developers of client or native client, we think much less about data flow than front-end students, such as Android livedata and fluent It can be seen that the development of such components has always been dominated by large factories. We have learned such a process. However, the front-end field still appears Vue.js Although there are Google angular and Facebook react in the framework of this kind of “non-governmental” organizations, the civil power can not be underestimated.

In fact, many of Android’s livedata / lifecycle refer to the react programming model, let alone flutter. The API design and documentation have all said that they are products of react model. It seems that the concept of componentization and state of react has been deeply rooted in the hearts of people (large factories). In addition, react has Redux and flutter hasfish-reduxIt also solves the problem of state management.

The worry is that the native side is far from being well managed by livedata / lifecycle. When recyclerview is used with paging library, loading more of this action can’t be notified to the global. Swiftui for IOS / MacOS is far away.. No, you know, native make complaints about a long way to go.

React the programming paradigm of the end

summary

I hope I can attract more valuable ideas and knowledge popularization. As a new Mac OS developer these days, I can’t stand the super multi-layer delegate, so I suddenly miss the feeling of writing DVA two years ago. I hope swiftui can mature as soon as possible, but at the same time, we also hope that apple can innovate as much as possible from MVC, which is a very stable (lost) design pattern, and bring a refreshing feeling to more developers. Otherwise, why do you prevent flutter from publishing applications in the app store?

Welcome to my official account, “TalkWithMobile”.
React the programming paradigm of the end