📜 ⬆️ ⬇️

Is it possible without Redux?

Today you can find a lot of positions where react / redux is required. React is beautiful, no questions. Question to Redux - is it possible without it. If you google a little bit, there is a good article on Habré (https://habr.com/ru/post/350850/), where the author asks the same question. In an article using a simple example (todoList), the method this.updateViews () is called too often (seven to eight times) and it seems that it can be made easier.

The main idea here is observable models, react is responsible for observable, it remains for the small - to create a model.

Before creating a model, a few words about the client’s design (architecture):
')
index - raw data
history - array [model]
observer - model
view - errors, focus, flags

index.jsx - entry point of the program for the user's screen. Index draws all components with default data, makes asynchronous requests, redraws components with new data.

// index.jsx <History> <Observer> <HeaderView /> <MainView /> </Observer> </History> 



Observer.jsx is responsible for synchronizing the model for multiple views. For example, Peter fills out an offer form and sees a real-time preview in the header of the page. Observer stores a model object, providing api: onModelChange (field, value) to its child components.

History.jsx is a stack of model objects, where api is commit and rollback.

Model.js - this is what the user can enter pens - that is, the most valuable. No other data is required in the model. Model is not a react component, but an ordinary js class.

 class Model { constructor(other = {}) {} // copy constructor (and default too) isEqual(other) {} // operator == less(other) {} // operator< swap(other) {} hash() {} fieldNameConstrains() {} //see below please } 


The copy constructor is at least needed for History. The isEqual method is for popup-unsaved-changes (which is much more convenient than the flag in the state). The fieldNameConstrains method is for dependent fields.

Roughly speaking, if there are dependent fields, they need to be changed by a whole crowd.

 class Model { // constrains // distance <== velocity * time velocityConstrains(newVelocity) { this.velocity = newVelocity; this.distance = this.velocity * this.time; } timeConstrains(newTime) { … } distanceConstrains(newDistance) { this.distance = newDistance; this.time = this.distance / this.velocity; } } 


From personal experience, something like model.field.onchange does not work, because sometimes you need to call the copy constructor and onchange events are not needed at all.

View.jsx
 class View extends React.Component { state = { errors: {}, focus: {}, … } render() { … <input value={this.props.model.title} onChange={e => this.props.onModelChange('title', e.target.value)} /> … } } 


Validation. Validation does not need to be done in the model. It should be carried out in view (do not forget that view is a user's screen and that not the whole model can be shown on the screen). Validators are a set of predicates. There are only two algorithms for validation: 1) we find all the errors in the form or 2) we find the first error. For example,

 class View extends React.Component { onClickSaveButton() { const mapper = { title: () => model.title.length && !maxLenValidator(model.title, 25), price: () => !(model.price % 40 == 0), url: () => !urlValidator(model.url), … } const errors = map(mapper, (validator, key) => { return validator() ? key : undefined; }).filter(Boolean); } //       


Access rights. The main thing is to stay and not use inheritance. The idea is that the model contains all the fields and we are cutting the fields for roles. That is, it is whitelist, the rest of the fields in the model remain by default. For validation, one step is added - we make a projection of the validation object (also known as mapper, see above), that is, we validate only the required fields.

About production. This approach has been in production for a year now - this is an interface for creating advertising campaigns, including banners. Forms of different complexity - from one model to the screen, ending with a variety of models of different types. Here you can add that the backend likes to send nested structures, you need to feel free to and keep only flat structures in view.

O method model isEqual. Somewhere in utils.js are the isEqual and isEqualArray methods
  function isEqual(left, right) { return left.isEqual(right); } isEqualArray(v1, v2) { if (v1.length !== v2.length) { return false } for (var k = 0; k != v1.length; k++) { if (!isEqual(v1[k], v2[k])) { return false; } } return true; } 


You need to try not to make models nested. Do not forget that the model is the user data, not the data structure.

References:
parasol.tamu.edu/~jarvi/papers/gpce08.pdf
stlab.cc/tips/about-mvc.html

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


All Articles