Why does React emphasize on unidirectional data flow and Flux architecture?

The official docs say that it gives you the control over how data flows throughout your app. I didn't get this. How is this principle violated if I decide to support two-way data binding and other MVC concepts?

Start a personal dev blog on your domain for free and grow your readership.

3.4K+ developers have started their personal blogs on Hashnode in the last one month.

Write in Markdown 路 Publish articles on custom domain 路 Gain readership on day zero 路 Automatic GitHub backup and more

Sandeep Panda's photo

Unidirectional data flow is a technique found in functional reactive programming. React doesn't encourage bi-directional binding to make sure you are following a clean data flow architecture. The major benefit of this approach is that data flows throughout your app in a single direction and you have better control over it (we'll see how). Also the application state is contained in specific stores and as a result different components of your app remain loosely coupled.

Two-way Binding Vs Unidirectional Data Flow

In two way data binding the view is updated when the state changes, and vice versa. For example, when you change a model in AngularJS the view automatically reflects the changes. Also an input field in the view can mutate your model. While this works for many apps, it can lead to cascading updates and changing one model may trigger more updates. In this case the data flow looks like following :

View <> State

As the state can be mutated by both controller and view, sometimes the data flow becomes unpredictable. Flux architecture shines here.

Flux takes a functional approach. The view is a function of the application state. As a result when the state changes the view automatically re-renders itself. In other words, the same state produces the same view which gives you better predictability.

Mutation of data is done via actions. So, new data always enters into the store through actions. View components subscribe to the stores and automatically re-render themselves using the new data. So, the data flow looks like this :

Action -> Store -> View

Flux Dispatcher

As you can see, the data flows in a single direction from parent to child making it much more predictable. As you have a single source of truth you can easily debug the app in case something goes wrong. This can be difficult is a large MVC app where the model can be mutated by any component.

Here are some links that may help you :

James B's photo

Two way data binding leads to updates that are unpredictable. Imagine how Angular handles this - through dirty checking! This is super slow and counter productive - not to mention the max 10 apply cycle limit. Although this will be solved via Object.observe in Angular 2.0, in my opinion bi-directional binding is not the best way to handle data flow in most cases.

By keeping the data flow unidirectional you keep a single source of truth i.e. Store. Your views are just the functions of the application state. Change the state and your view changes. This is way more predictable and gives you a clear idea about how different components react to state change.

Michael J. Ryan's photo

It's about managing complexity as you add features. In the end, a unidirectional data flow, especially combined with a single store and single state tree (redux, socrates and similar) which distill from the flux pattern, mean that changes to state happen in a consistent manner, and that actions/events are also dispatched consistently.

In this way, there is more cognitive overhead getting started in a new project, because the pattern breaks typical binding on components and separates out your control flow away from controllers and towards reducers. In the end, as you add features, new data, new containers/views/components/controls, you don't add much new complexity, and the logic is easier to manage.

It's hard to get into this mindset when you start a project, and the "community standards" are evolving... but it's easy to see how this works better in larger applications. In a simple TODO app, not so much, but in very large line of business apps, absolutely.

Anecdotal example, At work, I'm currently supporting three applications. One is an angular 1.x line of business application that is customer facing, it is very component/service centered and making changes to the workflow are very hard, to say the least... Another is a react+redux proof of concept for the prior application. The proof of concept handles roughly half the workflow of the original app (designed for the portion of workflow), but the codebase covering that workflow is a fraction of the size, and a lot of the larger complexity is gone and significantly easier to follow. The third is a new application using Angular 2, but it's going to use a single store/state tree. It's in Angular 2 (political reasons) as handed down, and unidirectional workflow, single-state based because it's proven itself out to reduce total complexity with additional features.

A bit of advice, divide up your structure (actions, reducers, controls, components, views, containers) based on feature, not based on types at a higher level. When you need functionality that is a cross-cutting concern, expose hooks via each feature's top level index, and use that for bridging the gaps.

Stephen Roth's photo

Ember developer here and Ember is a strictly conventional MVC so I think I can possibly answer your question. From what I can gather its away to decouple yourself from the expectation of two way bindings and some of the issues it causes when the view is so entangled in the store/data structure. Being able to trigger actions and dispatchers that then go on to update a view is the what Flux (to me) is in a nutshell. Given the shadow DOM and the benefits of managed component rendering in React 16 (Fiber) I don't see how they can occupy the same development architecture. Having this dispatch and event pathway lets the components compute and render separately and that is ACTUALLY better for a front-end application in terms of render and load speeds. IMO: Two way bindings cause data store changes in such a closed loop that there is little possibility for the kind of innovation that React and the various stores are bringing to the table.