📜 ⬆️ ⬇️

Future with proxy

image

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!
')
image

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.

image

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

image

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 StateManager
class 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.006ms

Proxy
~ 0.05ms

This 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> 


image

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

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


All Articles