We programmers are
dreamers . We go on about HYIP, dreaming of a new silver bullet that will solve all our problems. And also, we love to write new bikes, thereby not solving problems, but creating new ones. Let's talk about architecture a little bit in this article by developing a “Pseudo-new” bicycle.
Let's try to develop our architectural solution using modern approaches, with an eye on the older brothers
Redux ,
Mobx , etc.
Being in a wonderful time in which no one previously needed JS, able to just animate DropDown, became an unexpectedly powerful language! With a bunch of interesting, new features and rethought "old" features. More and more often, we do not go head-on, but we design our architecture, use Paters and solutions that can facilitate development and create scalable and easily digestible code.
So, let's begin!
')
At once I will make a reservation that in our bike we will solve the issue not of rendering View, but of storing data and reactions to their changes.
The future is now! We have
ES2015 + ! We can work wonders with him! But wait ... And what does he give us? So that we should take and write our new super-bike, which will certainly have to solve all our problems?
Let's look at the
proxy !
Proxy is a way to track operations on an object, such as deleting, modifying, and so on. Now
Proxy is supported everywhere except IE11, Vue3 under the hood will use it. Well, the worse we are, there is nothing to produce legacy, let IE11 remain behind our galley, we will sail to a bright future. In short, for cycling the first ingredient will be -
Proxy .
But
Proxy does not solve all the problems, and should not solve it, just a way to track the interaction with the object. Let's look at the best practices of our favorite solutions.
Redux , what do we love about him? Single data storage for our application can handle changes in one place, without smearing the logic in all parts. This is a plus, take it! Middlewares, they are able to expand and supplement the functionality, organize logging, proxying, etc. Wonderful! But the main advantage of
Redux 'a, as for me, is simplicity. So we, in our bike, need to do everything as simple as possible.
Of the minuses, this is work with asynchrony. A lot of extra code that you have to write ... We must somehow avoid it.
Mobx is a great solution for keeping track of changes. This will be our
Proxy . The only thing in the future, teach
Proxy to work with nested structures.
Typification . It would be nice to use this popular topic and even add to it. We will make typing and validation in runtime in our super solution, taking into account nesting.
RxJS , I love him for the fact that in many cases he is able to solve the most complicated tasks for me, but we don’t need to implement it. We will try not to complicate our decision, but to use ordinary objects JS and maybe
Rx will work with our bike as it should.
React , here I would like to stay in more detail.
React , this is a crucial framework. Before
React 'a, we had Ember and Angular 1, which gives a primary view of the component approach. We wrote a lot of components and directives, complementing the basic logic that was concentrated in controllers, services, etc. But with the advent of
React 'a, we began to consider our application more declaratively, without running away from HTML, in the same context and we can say the syntax.
The declarative nature of the component approach is what we need to achieve in order for everything to be as transparent and clear as possible. Fashionable
render-props now provide an excellent opportunity not to get out of the flow and write logic even more declaratively than HOCs, so take them.
After we have approximately defined how it should look like this, let's start writing code
Each modern framework is good in its own way. We are now solving the problem of state storage, in fact, we should use it anywhere. In this article, for example, I chose
React , because I know it better. But ideally, it should be universal.
Let's create the StateManager component. This is the main component that stores all application states, like
Redux . Optionally, we can add validation (for example,
this solution ) and typing (TypeScript / Flow). And also, optionally, write
Middlewares functionality that will work out the changes before, after, during (placing their call in Proxy).
component StateManagerclass StateManager extends Component { componentWillMount() { this.data = this.props.data || {}; this.proxify({props: this, val: 'data'}); } proxify = ({props, val}) => { Object.keys(props[val]) .filter(prop => typeof props[val][prop] === 'object' || Array.isArray(props[val][prop])) .map(prop => ({props: props[val], val: prop})) .forEach(item => this.proxify(item)); props[val] = this._makeProxy(props[val]); }; _makeProxy = props => { return new Proxy(props, { set: (target, key, args) => { if (typeof args === 'object' || Array.isArray(args)) { target[key] = args; this.proxify({props: target, val: key}); } else { target[key] = args; } setTimeout(this.forceUpdate.bind(this)); return true; }, deleteProperty: (oTarget, key) => { setTimeout(this.forceUpdate.bind(this)); return true; } }); }; render() { return this.props.children(this.data); } } StateManager.propTypes = { data: PropTypes.object.isRequired };
In order, what happens here. From the outside, we are forwarding the data of our application, or an empty object (schema) that we will fill in the process. Wrapping our application
Using StateManager <StateManager data={{ state: {} }}> {store => ... … } </StateManager>
The
proxify method recursively transforms all objects / arrays into
Proxy , making them susceptible to change, through the
_makeProxy method.
In the proxy object itself, I added 2 hooks —
set , for any change,
deleteProperty — for any deletion. In
set, we see if we have an object, an array, or deep data, we again run them through
proxify , after which we call
forceUpdate , which will redraw all the child elements with the updated state of our application.
Throwing the changed state through
render-prop , we can automatically scatter it into any part of the application.
Since, in fact, this is an ordinary object, we can fasten the same
Rx to it , or whatever, to mutate at any time, and generally pervert as much as it pleases.
By speed The object being proxied differs significantly from the usual assignment. I took measurements to create and assign a string to an object with a nesting depth of 2:
for an ordinary object~ 0.006msProxy~ 0.05msThis is a common assignment without a reactor and other things. Let's hope this is optimized.
With this primitive approach, written in a few hours “on the knee”, you can create a to-do list in which the data comes asynchronously and we can control the output of the elements
To-do list <StateManager data={{ state: {} }}> {store => <div> <button onClick={() => store.state = { todo: { items: [ {name: 'Get data', state: 'in-progress'}, {name: 'Bind proxy to all', state: 'in-progress'}, {name: 'Force update root component', state: 'in-progress'}, {name: 'Update data in children', state: 'in-progress'} ], filter: '' }} }>Load todolist</button> <hr/> <div> { store.state.todo && store.state.todo.items && [ <ul key="todolist"> { store.state.todo.items .map((item, index) => <li hidden={!(store.state.todo.filter === '' || item.state === store.state.todo.filter)} style={item.state === 'done' ? { textDecoration: 'line-through' } : {}} key={index}> {item.name} {item.state === 'done' ? <button onClick={() => store.state.todo.items[index].state = 'in-progress'}>Set to in progress</button> : <button onClick={() => store.state.todo.items[index].state = 'done'}>Set to done</button>} <button onClick={() => store.state.todo.items.splice(index, 1)}>Remove</button> </li> ) } </ul>, <div key="add-new"> <input type="text" ref={c => this.input = c} /> <button onClick={() => { if (this.input && !!this.input.value) { store.state.todo.items.push({name: this.input.value, state: 'in-progress'}); this.input.value = ''; } }}>Add new item</button> </div>, <select key="filter" onChange={e => store.state.todo.filter = e.target.value}> <option key="all" value="">All</option> <option key="in-progress" value="in-progress">In progress</option> <option key="done" value="done">Done</option> </select> ] } </div> <br/> <br/> <br/> <hr/> <h3>State:</h3> <pre>{JSON.stringify(store, true , 2)}</pre> </div> } </StateManager>

With this example, I would like to share with you my thoughts about using
Proxy and a few good practices. I hope that the use of new standards will inspire you to create full-fledged solutions that will adequately take their place in our JS zoo. Thank you all, good luck in cycling!
References:
→
Source Code→
To-do list