In the last week, I’ve been trying to make sense how two-way data binding (Angular) and one-way data flow (React/Flux) are different. They say that one-way data flow is more powerful and easier to understand and follow: it is deterministic and helps avoiding side-effects. In my newbie eyes though, they both look pretty much the same: the view listens to the model, and the model reacts on actions done to the view. Both claim that the model is the single source of truth.
Could anybody comprehensively explain in understandable way how they are really different and how one-way data flow is more beneficial and easier to reason about?
Angular's two-way data binding
It's made possible by a mechanism that synchronizes the view and the model whenever either change. In Angular, you update a variable and its change detection mechanism will take care of updating the view, and viceversa. What's the problem? You don't control the change detection mechanism. I found myself having to resort to ChangeDetectorRef.detectChanges or NgZone.run to force the view to update.
To not dive too deep into change detection in Angular, you trust it will update what you need when you change a variable, or when it gets changed after an observable resolves, but you'll find you have no idea how and when it runs, and sometimes it will not update your view after a variable changes. Needless to say, it can sometimes
be a pain to find where and when a problem occured.
React's one-way data flow
It means that the view always gets its state from the model. To update the view, you need to update the model first, and then redraw the view. React makes the view redrawing process extremely efficient because it compares not the actual DOM but a virtual DOM it keeps on memory. But how does change detection work in this dynamic? Well, you trigger it manually.
In React, you set the state's new value, which then causes a ReactDOM.render, which causes the DOM comparing/updating process. In React/Redux you dispatch actions which update the store (single source of truth) and then the rest. Point is, you always know when the stuff changes, and what caused the change. This makes problem solving quite straight forward. If your app depends on the state, you look at it before and after the action that triggered the change, and you make sure variables have the value they're supposed to.
Implementations aside
From a platform independent point of view, they're not so different. What separates one-way flow from two-way binding is a variable update on change. So your impression that that they're conceptually not too far from each other is not too divorced from their practical uses.
In Angular you have many controllers. One example would be a user triggering an action on View 1 that is managed by Controller 1. Controller 1 does something but also fires an event that is caught by another Controller 2. Controller 2 updates some property on the $scope and View 2 is suddenly changed.
Suddenly an operation on View 1, updated View 2. If we now throw in some Async callbacks and a bit more event chains, you might no longer know exactly when/how your views are being updated.
With Flux/Redux, you have a one way data flow. The view never updates the model, the views can only dispatch an action (intention to update), but lets the store/reducer deciding how to handle the update. You can more easily reason about the data flow because you can easily see which actions can be fired by each view. Then follow up to see how that action is being handled by the store and you can know exactly what can be updated.
Data flow here is a flow of write events - i.e. state updates
These events are flowing between views and controllers (and services, such as HTTP backends)
One-way flow is basically the giant cycle:
app view uses (reads, not writes) app state to render
when application gets some stimuli from outside (user typed some text in input field, or result of HTTP request has arrived), it emits write event - or, in Redux/Flux slang, dispatches an action
all events, from all controllers and views, are flowing into the single sink - dispatch function (reducer); although the nature of dispatch function allows it to be composed from simpler dispatch functions, conceptually, there's only one dispatcher for the whole app
dispatcher uses an event to figure out which part of the state is to be updated
go to start
Two-way flow aka data binding binds two pieces of state: in most cases, one inside the controller (e. g. some variable), and one inside the view (e. g. contents of textbox). Binding means that, when one piece changes, the other piece changes as well and gets the same value, so you can pretend that there's only one piece of state involved (while there's two actually). Write events are going back and forth between controllers and views - thus two-way.
Data-binding is cool when you need to figure out what variable holds the contents of this particular textbox - it shows immediately. But it requires complex framework to maintain the illusion of one piece of state where there's two pieces really. Usually you'll be forced to use framework-specific syntax to write your views' code - i. e. to learn yet another language.
One-way data flow is cool when you can leverage that extra entity - events flow. And, usually, you can - it's useful for Undo/Redo, user actions replay (e. g. for debug), replication, etc, etc. And the code to support this is much, much simpler, and usually can be written in plain JavaScript instead of framework-specific syntax. On the other hand, since you no longer have data-binding, it no longer saves you some boilerplate.
Also, see great visual explanation in this answer: https://stackoverflow.com/a/37566693/1643115. Single-headed and two-headed arrows visually represents one-way and two-way data flow respectively.
Let's say your app is just a wizard flow, but it has some complex interactions i.e. one step might change a following step behavior.
Your app is running great, but one day an user reports a bug on one of the tricky steps.
How does debugging would work on two-way binding and one-way binding?
Two-way binding
I'd start checking what behavior is different and with some luck, get to the same point as the user and pinpoint the bug. But at the same time there might be some weird interaction between different parts of the app. I might have some data-binding that is incorrect (e.g. replicating the model state but not binding) or other weird intricacy between components that is hard to debug. It might be hard to isolate the bug.
One-way binding
You just grab the state object. It has all the information of the app currently in a big javascript object. You load the same state in your development environment, there is a big chance your app will behave exactly the same. You can even write a test with the given state for regression and pinpoint the exact problem that is happening.
Conclusion
In a few words, one-way binding makes it very easy to debug complex apps. You don't have to do much then copy over the current state of the user.
Even that doesn't work, you can log the actions as well. There isn't AFAIR an easy way to track all the state modifying actions on Angular, for instance. With Redux it's pretty, pretty easy.
Related
Following the flux concepts we can get the next assertions for which I couldn't find explanations.
Every store will receive every action.
Why? My suggestion: since a store contents some business-logic we have to provide it with all possible changes and data so the store can decide what to do with them on its own.
The data in a store must only be mutated by responding to an action.
Why? My suggestion: the reason is violation of unidirectional data-flow in case of not responding to an action.
Every time a store's data changes it must emit a "change" event.
Why? I can't get this point.
Flux is just a way of managing the data flow of your application, so it is up to the developer to make sure this actually happens. But I'll try to paint a picture of why these concepts are a part of Flux.
Every store will receive every action.
If you have only one dispatcher in your application, every store will listen to actions dispatched through that dispatcher. It is up to you whether or not the store should act on the action dispatched, but to be able to react on it the store has to know of it.
Not all actions should lead to changes in a store, though. But the dispatcher simply doesn't care, because it won't know anything about the store implementation. It's just telling all stores that this action happened, do what you want with it or go on with your life without caring.
The data in a store must only be mutated by responding to an action.
You're right that doing it with a different approach could be violation of unidirectional data-flow. Doing things this way makes sure all parts of your application has the correct state based on the actions that happens.
By not doing it this way you would let go of one of the flux strengths. Update your store based on dispatched actions, and other stores will also be aware that the action happened, and thereby react to it if they want to. If you update the store directly you will end up having no clear picture of what parts of your application that are altering the state of your store.
Every time a store's data changes it must emit a "change" event.
People often describe the stores in a flux application as the source of truth. When a store's data changes, the basis for the visualization of your data changes. You want to be confident that if my store holds a certain value, this is what my application uses as it's data.
It's related to the first quote here. The store doesn't know if a listener depends on it's data. By emitting a change, it will let all listeners know that hey, I changed. Make sure you have all my latest changes. If you don't emit change, the listener could end up displaying something based on old data.
All of these statements are related to the same thing: If an action happens in your application, don't make any assumptions about which part of your application that wants to know the details of it. Make sure everyone can act on it, if they want to.
I'm looking for some guidance with regards to managing state in a real-time messaging/chat app built with VueJS 2.
The app consists of several components which are outlined in the following diagram:
So far, I've implemented displaying (fake) conversations. The App component contains an array with conversation objects. For each child component, the relevant data is passed using props. This is really simple and works like a charm.
Now, I have to deal with actions/mutations from components deeply nested in the tree. For example, sending a message and appending it to the corresponding array of messages.
I figured it would be as easy as dispatching a (global) event in the AppConversationChatWindowInput component and handling it in the App component. Boy was I wrong. Apparently, this functionality was removed when Vue 2.0 was introduced in favor of Vuex. I'm not sure why it was removed, because in some situations this could be a perfectly reasonable way to deal with events.
I guess there are a couple of possible solutions:
Passing the websocket connection to each child component. This could technically work. The App would connect to the websocket server and pass this connection to its child components using props. When the user sends a message, it is echoed by the websocket server. The App component can listen for the message and append it to the array of messages.
Regardless of the technical feasability, this feels like a crappy and hard-to-maintain archicture to me. In my opinion, no component other than the App should be aware of the websocket connection, let alone its concrete implementation.
Manually bubbling up the event in each component in the chain.
This seems like a complete pain to maintain. Introduces a lot of needless complexity and failure points.
Using a global event bus.
This is possible, but why should an input field depend upon a global event bus? I don't like unnecessary dependencies and coupling. It adds complexity and makes things harder to test.
Using a global data store (Vuex).
See #3. Another dependency and added complexity. Also, if I would settle for Vuex, how would I retrieve data in my components? Do I pass it down using components (like I do now) or would a component deep down in the tree just grab it from the store directly? To me, it feels like the component knows a lot more than it should this way.
Any thoughts? What's the best way to handle state in my situation?
There's a bit of a disconnect between "I wanted to dispatch a global event" and "I don't want to use a global event bus." A global event bus is how you dispatch/broadcast a global event. It is, as you note, a good solution in some situations. It is not hard to set up when you need it, so there's no strong reason for it to be in core Vue.
You can create the bus as an instance property on Vue so it is available to every component:
Vue.prototype.$globalEventBus = new Vue();
Where you would have had vm.$dispatch(...) you would do vm.$globalEventBus.$emit(...) and the receiving component can set up vm.$globalEventBus.$on(...).
Alternatively, you could create a bus at the top level and pass it through the children as a prop. This avoids globals, and you don't have to worry about bubbling.
Finally, as I noted in my comment, native events bubble, and you can catch them at any component higher up the chain. You could catch the event(s) that send messages, or even roll your own events to catch.
I am thinking about how to organize Model in my webapp. I have two options, as I see it:
Create a single globally available Model object, then on instantiating new components they must register themselves in the Model object via methods, then update it when their state changes. Others would listen to changes from the Model by some key.
Create a model object for each component and let others know via some Event dispatcher about their state change.
In 1st case I feel like it will be easier to manage all the state change in one place, have more standardized way of doing it, while with the latter scenario I think it will be harder to maintain consistence over the system.
What are the factors I should base the decision on?
I would go with the first but adding the idea of the second: Instead of the components checking constantly the keys for changes you can make the only model signal the components when needed after they register. You can pass the signal/callback with the register itself.
That way you don't need to create an army of models that update themselves (that might also mean you have to avoid cyclic propagation). In addition to this, if I understand correctly it would violate the single source of truth. Also it looks worse both memory and performance wise.
I'm implementing a JavaScript-based Vaadin component that will need to show and update a relatively large data set. I'm doing this by extending AbstractJavaScriptComponent.
I'm trying to keep the JS side as "dumb" as possible, delegating user interactions to the server using RPC, and which updates the shared state. Then the JS connector wrapper's onStateChange function is called with the new state, which causes the DOM to be updated accordingly.
I have 2 problems:
I don't want to transfer the whole data set each time a small part gets updated.
I don't want to entirely rebuild the UI each time either.
I can solve the second problem by keeping the previous state and comparing parts of it to find out what changed and only make the necessary DOM changes.
But that still leaves the first problem.
Do I have to stop using Vaadin's shared state mechanism and instead only use RPC for communicating the changes to the state?
Update:
I've been doing some testing, and it certainly appears that Vaadin's shared state mechanism is horrible in terms of efficiency:
Whenever the component calls getState() in order to update some property in the state object (or even without updating anything), the whole state object is transferred. The only way to avoid this, as far as I can see, is to not use the shared state mechanism and instead use RPC calls to communicate specific state changes to client.
There are some issues with the RPC approach that will need to be resolved, for example: if you change a value multiple times within a single request/response cycle, you don't want to make the RPC call multiple times. Instead, you want only the last value to be sent just like the shared state mechanism only sends the final state in the response. You can keep dirty flags for each part of the state that you want to send separately (or just keep a copy of the previous state and compare), but then you need to somehow trigger the RPC call at the end of the request handling. How can this be done?
Any ideas on this are welcome!
Update 2:
Vaadin 8 fixes the root issue: it sends only the changed state properties. Also, it doesn't call onStateChange() on the JS connector anymore when only doing an RPC call (and not changing any state).
OP is correct in stating that shared state synchronisation is inefficient for AbstractJavaScriptComponent-based components. The entire state object is serialised and made available to the Javascript connector's onStateChange method whenever the connector is marked as dirty. Other non-javascript components handle state updates more intelligently by only sending changes. The exact place in the code where this happens is line 97 in com.vaadin.server.LegacyCommunicationManager.java
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
I'm not sure why state update is handled differently for AbstractJavaScriptComponent-based components. Maybe it's to simplify the javascript connector and remove the need to assemble a complete state object from deltas. It would be great if this could be addressed in a future version.
As you suggest, you could dispense with JavaScriptComponentState completely and rely on server->client RPC for updates. Keep dirty flags in you server-side component or compare old state and new state by any mechanism you want.
To coalesce the changes and send only one RPC call for each change, you could override beforeClientResponse(boolean initial) in your server-side component. This is called just before sending a response to the client and is your chance to add a set of RPC calls to update the client-side component.
Alternatively, you could override encodeState where you have free-reign to send exactly whatever JSON you like to the client. You could choose to add a list of changes to the base JSON object returned by super.encodeSate. Your javascript connector could interpret as appropriate in its onStateChange method.
Edited to add: calling getState() in your server-side component will mark the connector as dirty. If you want to get state without marking it as dirty then use getState(false) instead.
Following our discussion about this, I've created a drop-in replacement for AbstractJavaScriptComponent that transmits state deltas and includes some extra enhancements. It's in the very early stages but should be useful.
https://github.com/emuanalytics/vaadin-enhancedjavascript
The solution is deceptively simple: basically re-enabling state difference calculation by bypassing this line of code in com.vaadin.server.LegacyCommunicationManager.java:
boolean supportsDiffState = !JavaScriptConnectorState.class
.isAssignableFrom(stateType);
The implementation of the solution is complicated by the fact that the Vaadin classes are not easily extended so I've had to copy and re-implement 6 classes.
Vaadin's shared state works exactly like you want out of the box: when a component is added to the DOM first time, the whole shared state is transferred from server to client, so that it's possible to display the component. After that, only changes are transferred. For example, one changes the caption of a visible component by calling component.setCaption("new caption"), Vaadin only transfers that new caption text to client and "merges" that to the client-side shared state instance of the component.
I'm new to ReactJS and I'm trying to figure out how to store and manipulate global state. For example, I'm writing an editor app that has some global state: selected color/background, active tool, current selection, etc.
I'm thinking about having a root interface component to store this information, and I'm ok with explicitly passing the state around using properties. I guess the idiomatic way for a children component to change global state is calling a callback received from the parent - personally I find this a bit annoying.
Instead, I'm firing custom events at the children and setting listeners on the parent. So far it is working really well, but I looked at a lot of sample code and never saw people using this pattern.
Is there any practical consequences I should consider?
Flux is great for manipulating global state because the Stores are globally accessible. Globals are known to be bad, but the way you interact with the Stores is through Actions. Actions make the state predictable and localized. So even though the state storage itself is global, Actions make the state interactions very local. At work we tend to do both reading & writing through Actions, so every state change is an "event" that leads to easily programmable behavior.
I really wouldn't compare Flux to Angular as their principles and flows are entirely different. Suggest you try Reflux.