Question: Reconciling One-Way Data Flow With Browser-Based Navigation
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.
|
|
|
||
|
|
|||
|
|
|
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
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.
@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?
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.
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.