I want to start a series of notes about the Lition front-end. On one side we offer several interesting things in our client app like author’s approach to the organization of unidirectional data flow (UDF) in React.js + Redux, from the other side these kind of things are very simple and are, from my perspective, elegant. Let’s see together.
Attention! There will be many examples of code that can’t be copied, if it annoys you, then immediately close this note.
Lition is a global project with distributed team which cover several countries. The front-end team is located in Grodno, Belarus.
A few words about the project and the idea. Lition is a web trading platform that allows consumers to purchase electricity directly from producers — energy providers. Providers can be either large sellers — public or private power plants, as well as smaller producers which have for example small farms of solar batteries.
The platform provides the consumer with all the necessary information about the producer, including the type of electricity — this is how it was obtained: bio, solar, wind, etc. The purchase of electricity is carried out through the popular blockchain platforms — Ethereum and Hyperledger (not fully, but we are in progress).
The trading platform is unique and very interesting — it is a real revolution in the energy sector. More information about the project can be found on the Lition site — https://www.lition.io.
Recently we have had a business trip to Berlin, to our customer’s head office. The guys exchanged experience and knowledge on the project. At the moment we are trying to erase the boundaries among the front-end, back-end and blockchain teams to get one monolithic agile team which is responsible for supplying the whole product not just some part of it.
I believe that we will succeed!
Client Architecture: React.js + Redux
The entire architecture of our client application is built on a simple concept, which I described in a series of my previous notes (here [eng], here [rus] and here [rus]). The idea already underlies several projects that are at the production stage and are time-tested. We used this concept in different projects, and after that we slightly improved our approach due to our experience.
The only thing left of my “bike” (Archux) in this project is Dispatcher — a tool that solves the problem of side effects. Again, no gadgets such as thunk, saga, rxjs, or anything that we do not need. The main goal is KISS — Keep it simple, stupid. The threshold of input to the code base should be minimal, both for the junior developer and for the senior pro. Everything should look as simple as possible. There is no need to complicate the application if there is no reason.
Simple is better than complex.
Complex is better than complicated.
Zen of Python, June 1999 by Tim Peters
All Actions of the user pass through the Dispatcher. Actions are initiated with the help of performers — Action Performers. State management occurs through Redux, i.e. as I said, my “bike” in the garage. One of the important points why this was done is the various plugins to the browser that work only with Redux. Yes, and psychologically, programmers are easier to accept this approach when they know that there is a popular Redux, and not a “bicycle” of a certain teamlead who suffering from NIH-syndrome.
But the concept itself does not break, the approach will be fair for any library that allows you to work with the state of the data store — Archux, Redux, Flux, Fluxxor …
React-components are divided into 2 types: Containers and Presentational. Containers subscribe to the application Data Store and most often it’s a full web pages of our applications. Presentational components are closer to the implementation of Web Components, these are separate standalone controls or a mapping that does not depend on our application in general. All presentational components are described in the interactive documentation that we deliver with the client application.
Ok, words, words. Talk is cheap. Show me the code, — said Linus Torvalds :)
So, I show. Consider one cycle of a unidirectional data flow with all it’s steps.
The user agrees with the presentational component — a simple button "Select Producer":
On the energy producers page, we have two buttons, one of them allows the end user to select his electricity provider. By the code it is clear that this button calls a certain performer of the action (“performSelectProducer”).
Performer initiates the action through the dispatcher, and also associates this action with the asynchronous service — in the example, this is a request to the server. Below is the code for this action performer:
An API service that generates HTTP requests through the Axios library:
The code of the dispatcher:
After a successful external asynchronous operation, the dispatcher sends a response to the data store. The store and the dispatcher are implementations of “Singleton” design pattern, they exist only in one instance.
Then Redux enters the case — it sends a response to the Reducer.
We do not have the concept of an action with an error, action-successful or action-pending. The user action is one and it has the corresponding name without any silly prefixes or postfixes.
However, the information in action can be a different: an error message; payload data; indicator that the action is asynchronous and it is necessary to wait for an answer; metadata — input arguments — very useful for keeping identifiers.
Container that is subscribed to the data store and to change it’s state, within the corresponding method of component life cycle, responds to the entry in the data store — initiates the appearance of the notification (this is a new round of the unidirectional data flow) and moves to another page — “Overview”.
Our routes are configured in the corresponding service. Also there an instance of the route history object was created in the services.
During reading the code, you probably found 2 things:
- The container is inherited from some strange class AbstractContainer — it's smelled like Object-Oriented Programming;
- And some strange intl property in the context of the container;
I agree, non-obvious things. Let’s talk about this. AbstractContainer is the parent class for all container components. It’s inherited from React.Component. In fact the abstract class is a combination of different mixins that merge common functions: installation of breadcrumbs; preparation of the text structure / labels, according to the selected locale; work with the scroll bar of the page, etc. We used this technique earlier, on our previous projects.
The intl property in the context appears thanks to the provider, responsible for the internationalization (i18n) and localization (l10n) of our application.
At the moment we have two languages (German and English), but the plans for the future is to add a few more.
The solution for localization is not custom, it’s an integration with popular React-Intl library from Yahoo! All translations are stored in the corresponding JSON-files, also we make the necessary settings for the library moment.js — works with the dates and timezones. And for other places where special formatting or conversion is needed we use EcmaScript6 Intl API and in our JS-logic we based on the HTML lang attribute.
Locale changing bind to the Data Store. Those everything is done within the scope of the following Action. For a part of the semantics we do not forget in the right places to correctly apply the HTML attribute translate.
Thank you for attention and in the next note I’ll try to describe how we develop layout of our app.
P.S. If you have any ideas or thoughts please feel free to share with me.
2018, Artur Basak, Lition Tech Team