Skip to main content
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Brian Meloche
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Brian Meloche

Question: Reconciling One-Way Data Flow With Browser-Based Navigation

By
Published in Comments (4)

In this day and age, if you are building a non-trivial web-application, you are almost certainly using the browser's URL to manage the rendering and traversal of the application view. In such an application, the code both reads from and writes to the browser's URL. The application may change the URL in response to user-actions, such as clicks and swipes; but, it may also change the URL in response to background events such as WebSocket messages or failed data fetches. None of this is new information; but, the part that I'm having trouble with is the reconciliation of this standard workflow with the concept of a one-way data flow.

Imagine I have a UI (user interface) widget that renders a list of items and I want to change the state of the application when one of those items is selected. In a one-way data flow, I might have some AngularJS code that looks like this:

<my-item [item]="item" (select)="handleSelection( $event )"> ... </my-item>

Or perhaps some ReactJS code that looks like this:

<Item key={ item.id } item={ item } onSelect={ this.handleSelection } />

In this case, the "handleSelection()" method is called when the user selects the desired item. And, the application can then change the state based on which item was passed-in with the event.

This works well for certain types of UI widgets; but, it's not really appropriate for widgets that render traditional anchor tags.


 
 
 

 
When I right-click on your  
 
 
 

For better or worse, people have an expectation of how the browser should work. And, things that look like links should act like links. Meaning, you should be able to CMD-Click a link or right-click and select "Open Link in New Tab" to have the browser open a new view with the given Location. Of course, not all links drive the URL; but, the ones that do drive the URL should operate in this manner.

NOTE: There's probably an "accessibility" (a11y) issue here as well; but, I'm sorry to say that I don't have the best mental model for accessibility.

But, the problem with this standard and expected behavior is that it allows the UI widget to change the state of the application (for all intents and purposes) outside the bounds of a one-way data flow.

How are people reconciling this? Are you just shrugging and saying, "Because browsers"; or, is there some way to reason about this workflow such that it doesn't conflict with a one-way data flow architecture?

I'm not being rhetorical - I'm truly curious. I don't have a sufficient answer.

I also have a lot of confusion around how much of the application should even know about routing to begin with. For example, if a data fetch comes back with a "404 Not Found" response, what part of the application is "allowed" to redirect the user (via the browser URL)? My gut says that only the traditional "Controller" / "Smart Container" should do this; but, this requires workflow implementations (or Sagas or Thunks or whatever your particular community calls them) to expose events or callbacks that the Controller can hook into.

But that's a whole other conversation.

For now, I'm just curious as to how people reconcile the use (and expectation) of traditional anchor tags within the context of a one-way data flow architecture. What is the perspective that I'm missing? Where is the gap in my thinking? Or, is this just the state of web application programming?

Reader Comments

34 Comments

Sounds like poor or shortcut development, depending on the situation. Routes and History API controls can be used to allow normal browser interactions and use of "real" links where necessary.

15,848 Comments

@Jon,

I am not entirely sure I understand what you are saying. Are you saying that having normal browser links is good or not good?

1 Comments

Maybe I haven't understood the question, but this is a non-issue in Ember apps.
The URL contains some serialized state. In a properly structured app, that URL should drive the entire navigation. If at any point, in any page, the user decides to hit F5 and he/she doesn't see roughtly the same thing we was seeing before the refresh, your app is fundamentally broken.

As long as the information that the user is seeing is not dependent on the session, you should be able to share a link via Slack with any other coworker, open it in a new window or email it to your mother.

There is some transient state that probably doesn't worth serializing in the URL, like whether or not a sidebar is expanded or collapsed, or the values of a half-filled form. It is doable but probably overkill. But at least the user should be taken to the same resource.

34 Comments

Having links is good, at least where links make sense, and Angular and the rest can use links to urls attached to routes in most cases, it's just "easier" in most cases to not build a link, so developers don't.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel