📜 ⬆️ ⬇️

Vibe.js - an attempt to make state management without pain

All yo, habrazhiteli.


In general, it happened that I have been writing in JavaScript for quite a long time, and one of the most important tasks has always been the organization of the application state.
Tl; DR;
There is nothing more familiar in the world
How to write a wheel on a bicycle


I want to cache something, update something, and update everywhere, and not just in the local component, I don’t want to redraw the entire component if the entire Store has changed (shout out to Vuex), but I want to subscribe to what you use (shout out to MobX).


I really disliked several aspects of Redux:


1) Too much boilerplate code. Of course, there are many ways and approaches to make mutations more pleasant for programmers, but nevertheless, all the same, this part is overloaded with IMHO.


2) Fragmentation of entities. At one time, when I wrote mobile applications on ReactNative, we worked with the JSON API server, that is, it returned an answer in the json api specification format, including the entities and relations of these entities. I really liked the specification, although at first I did not dub. And immediately an example of a problem: we have a list of dialogs, we entered a dialogue - the user is online there. Returned to the list of dialogs - user is offline. I think it is familiar to users of the VC.


In Vuex, in principle, I like everything, but the problem of the fragmentation of entities is not solved there are nuances.


What is the idea of ​​Vibe.js


When I wrote the proof of concept, I was repelled by the following ideas:


1) I want the entity to be in one place. As in the database: 1 id = 1 entity.
2) I want to be able to subscribe only to the necessary entities.
3) At the same time, I want to combine different entities and attributes so as not to turn up a bunch of subscriptions to the necessary entities each time.
4) I want to be able to directly reactively update the state - entity.name = "Vasiliy" , but at the same time be able to make mutations with payload and somehow debug mutations, at least, for example, by adding a text message to them.


What happened


Now in Vibe.js there are the following concepts:


Model, EntitySubject


A class that allows you to define an entity model.
Usage example:


 const User = new Model('User', { structure: { name: types.Attribute, bestFriend: types.Reference('User'), additionalInfo: { age: types.Attribute } }, computed: { bestFriendsName(){ return this.bestFriend && this.bestFriend.name || "No best friend :C" } }, mutations: { setName(newName){ this.mutate({ name: newName }, "User.setName") } } }); 

The constructor allows you to describe the structure of the entity, the calculated values, as well as mutations.
The structure can be described using , or nested objects.


I note that the username can be changed directly: someUser.name = "New name" ,
but mutations are a more standardized approach.


The model instance itself can do almost nothing - it only stores the structures from the constructor.


If we want to add an entity:


 User.insertEntity(1, { name: "Yura", bestFriend: 1, // sad when the best friend of yourself is you additionalInfo: { age: 17 } }); 

If any values ​​are not specified, the default null will be used. To use this entity now, we call the observe method.


 const entity = User.observe(1); const user = entity.interface; console.log(user.name) // -> "Yura" 

There is a nuance, yes? Too much to write to work with the entity. On the lines.


1) entity = EntitySubject instance. It subscribes to entity changes and updates the interface . You can also subscribe to it.
2) interface = Reactive interface for working with an entity. It has values ​​for the state of the entity, computed values ​​and mutations. It should be noted that if the entity does not yet exist in the EntityStore , then entity.interface will be `null.


EntityStore


This, as the name implies, is the entity repository. It stores all states, all observable , models and contains methods used by Model or Subject .


 const User = new Model('User', { structure: { name: types.Attribute, bestFriend: types.Reference('User'), additionalInfo: { age: types.Attribute } }, computed: { bestFriendsName(){ return this.bestFriend && this.bestFriend.name || "No best friend :C" } }, mutations: { setName(newName){ this.mutate({ name: newName }, "User.setName") } } }); const Store = new EntityStore([User]); 

We initialize our entity store with an array of models so that the EntityStore can then link all relationships, links, subscriptions ...


Directory, DirectorySubject


Directories are similar to singleton entities. They are not identifiers, they are static. Also, they do not need to be specified when initializing the EntityStore , because entities cannot subscribe to them. In essence, directories are "directories" that have some kind of local state in the form of attributes and an entity reference.


For example, if we look at books in an online store, then such a directory could be used:


 const Store = new EntityStore([Book]); const BooksList = new Directory('BooksList', { structure: { page: types.Attribute, searchWord: types.Attribute, fetchedBooks: types.Array(types.Reference(Book.name)) } }, Store); 

Directories also support computed values ​​and mutations, and you can also subscribe to them.


What about subscriptions?


Vibe.js is organized so that an entity or directory will be subscribed only to those entities that are currently in their state. That is, if we have an array of displayed elements in the directory and replace it, then after changing the state, it will be written from the entities from the previous array and will be signed only with new ones.


Well, all this is tested


Or not all. I love to write tests and I want to write more of them, because it seems to me that they are not enough. In that regard, they do not cover everything that can go wrong.


Links


Github library repository


NPM module


Repository with an example Todo list on react


Github pages with generated ESDOC documentation


')

Source: https://habr.com/ru/post/341126/


All Articles