TNG ventures out and succeeds

Between Oct 2020 and Jan 2021, Talent and Growth Department (TNG) decided to experiment and brought forth very different initiatives with respect to what it had been offering so far. We were very…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




When did my UI change?

Imaginary UI for possibly describing data changes through time (rather than food)

Specific data permutations (props) can be passed to a component and we can discuss with stakeholders if everything is looking the way it should.

I think a future step forward is finding better ways to understanding how we got to that specific permutation. When a specific screen is rendered using lots of complex data, what brought us there? What are the things that caused the props to change and become what they are now?

I think the main complexity here can be boiled down to finding better ways to describe data changes through time.

Let’s imagine a specific run of a component that looks like this:

Note that this is a simple diagram across the time dimension: setState(A) happens before setState(X) which happens before setState(E)

These are calls to React’s setState (which modify state and cause a re-render) within a single component. The calls are triggered by external events such as button clicks or HTTP/WebSocket responses.

During a run of a program the order of each of the calls is very clear: one can simply add a console.log() inside each of those setState handlers and clearly understand when each call happened in time.

The problem is that this order is only clear at runtime! Can we make this more apparent in the code instead?

What if setState(K) (from onResponse) depends on some data that was changed by setState(E) (from onClick)? Surely in the earlier runtime example we got lucky with setState(K) happening after it. Another run might not be in the same order.

The problem here is of course the one of shared state. setState calls are modifying state and they also depend on state for doing their things.

But more than the technical problems of race conditions and getting into states that we should not be in, I’m more concerned that one cannot understand this time-based flow of data in the code. Why must it only be clear at runtime? Can we not code in a way that models these time constraints? I’d like to be able to perform code reviews on how we modelled data changes through time!

Let’s imagine a simple component run where the user clicks on a button that shows a popup (setState(P)) and that causes both an HTTP (setState(H)) and WebSocket (setState(W)) server to return other data to be shown in the popup. Note that onClick , onHTTP and onWebSocket are simple prop event handlers passed to the component which call setState:

Now let’s imagine another run where WebSocket is faster than the HTTP response:

Problem with this run is that the WebSocket setState(W) depends on the data returned by the HTTP response, hence it depends on setState(H) setState(W) must occur after setState(H).

We can find a way to fix this by running perhaps a reconcileStateBetweenWebSocketAndHTTP() function after both setState calls. This function knows about our specific data dependencies use case; hence cannot be very generic. With large interconnected data dependencies this seems a lot harder to achieve.

Note that this isn’t a problem of React! The same example can be reproduced in any other language or framework. I’m just using React as an example to drive the mental model.

What if instead we could model this time constraint through code? We could have an array that models the fact that we want to have a specific order of calls:

Note that in the above example we are describing that we want P to happen before H which should happen before W.

Then in my event handlers instead of calling setState directly I can wrap them in a deferSetState:

Note how we are passing M, H and W to the deferSetState call. Also deferSetState is a made up name and has nothing to do with React’s Suspense.

deferSetState will look at our timeModel and it will call setState (effectively suspending the call) only if it respects our time order constraint.

Note this is a minimalist implementation (which I wrote directly on medium so forgive me for any obvious bugs).

The idea of deferSetState() is that you can call it in any order that you want. You can even start from the WebSocket call!

Here all the calls are scrambled around in time. But it doesn’t matter because our timeModel clearly states that it wants [P, H, W] to appear in that order; hence the callbacks will suspend until the order is met.

An interesting outcome of this is that the WebSocket-related setState will never be called if, for instance, the HTTP-related setState is never called.

The time model not only allowed us to fix bugs with race conditions and invalid states, but it also brought forward an important property: we described (in code) data changes through time.

We can look at the code and confidently tell our stakeholders that the Popup will open before the HTTP response UI change which will happen before the WebSockets UI change. Having the confidence that this is what will happen without running the program, and being able to communicate this, is extremely useful.

A stakeholder can ask: I’m seeing a Popup window with some data in it. Can you tell me where this data came from? Again, you can of course simply run your program and with the help of logs and stack-traces figure your way out. But remember we’re looking for ways to achieve this through code.

So far our time constraint is pretty simple: just an ordered array of events. But in reality we might need more fine-grained control. Let’s take a look at some more complex examples:

Live Sequence Charts is a language that allows us to describe events through time. Time passes from top to bottom.

Please ignore the syntax or objects from the language above. What’s more important is seeing the result: describing the order of events in more subtle ways.

For instance LSC1 shows how two events (or setState calls) can be called in any order after a specific event. This cannot be modelled or described using a simple array of ordered sets of events as we did in our earlier implementation. We can take some inspiration from the LSC language and model LSC1 this way:

The syntax above could be a way to model such time constraint.

So far this post is just me rambling about possible ways to describe data changes through time and how we can apply those ideas to UI development.

Imagine the power of looking at a timeModel for a React component and being able to understand what things can happen and in which order. I can also work my way back and look for a critical event such as ClickMissleAlertButton and make sure a ShowConfirmation popup appears before it just by looking at the model and changing it accordingly.

Thanks for reading 👋

Add a comment

Related posts:

ChromeOS SMB network file share with macOS

I have an unused Chromebit that I want to play videos stored on my macbook pro. “ChromeOS SMB network file share with MacOS” is published by Stephane Bounmy in Mac O’Clock.

Rojan Has An Iron Will

Rojan J. Pajarin was born with a cleft lip and palate in the Philippines. When he was five years old, Rojan received surgery for his cleft lip from a visiting mission group, but his cleft palate…

Editorial Escribiendo de Cabeza

La mujer es la representación de lo más completo que existe en el mundo, cada parte de su cuerpo la utiliza para sacar las garras en esta sociedad machista, pero lastimosamente sigue siendo…