Hacker News new | past | comments | ask | show | jobs | submit login

Whenever someone mentions "UI is a function of state", I think about transitions and animations. Is every frame in a transition also a function of state?

I tried adding a transition to a dialog box in React sometime back. When the user tries to delete some data, a confirmation dialog shows up. It disappears(with CSS transitions) when the user confirms. Deletion happens before transitions are complete and the user sees a different, unexpected state in the dialog for a flicker of time.

This is solved if all the data inside the dialog is actually a deep copy of state.

If this was not done via react, we could've prevented the dialog content from being bound to the state

Should UI really be a function of states?




I finally grokked `view = fn(state)` once I understood that the `state` argument encompasses all possible inputs. Your global store, each component's internal state, the text selection, the mouse cursor position, the user's cookies, the progress of an animation - everything listed in the original blog post is just state, and the `fn()` doesn't necessarily need to care where it comes from.

The reason this is counterintuitive is that UI frameworks insulate you from some types of state. When you write your own `fn()`, it's only receiving part of the state, and it's only defining part of the view. In the browser, animations are a good example of this; other examples include keyboard focus, the size of the browser window, and most of the behaviour of native form controls.


> Is every frame in a transition also a function of state?

The start and end state are functions of state. The animation itself is what the element itself does when it's in the process of changing (like kicking a rock down a hill). The scroll position of some text is like this as well, it's internal state that you typically don't need to reify as application state.

You could make these part of the state where necessary, but it often isn't.


I’d say there are two common patterns for implementing behaviours like transition animations in declarative UIs.

The platform and/or the engine that is doing the rendering of your declarative UI might provide facilities to describe animations declaratively as well. In web development, we have some support for animations in CSS. It’s also fairly common, if you use a component-based rendering library like React or Vue, to see some kind of Transition component, which typically wraps the UI components that should change during the animation and specifies the required transition behaviour via its properties.

More generally, application states can be part of the “state” that your UI derives from. The linked post gives several examples of this, but a very common one in web apps is having a loading flag when content has been requested from some remote source and isn’t yet available. Whatever code you use to fetch that content also sets the loading flag in the meantime. Then the declarative UI code just needs to check the flag and render whatever spinner or other effect it wants to show during that time.

The same idea can be used to implement general transitions/animations. Your state can include something like an “is opening” flag, to indicate that a transition is in progress, and for effects that need to vary over time, also something like a time index or percentage complete. The code responsible for the transition behaviour then sets the flag and starts/updates the timer throughout the transition period. On the rendering side, the declarative UI just needs to check if the flag is set, and if so, render the required effect for the current point in the transition.

The key point in this second pattern is that some part of your code that isn’t the main declarative UI is responsible for defining this behaviour via the application state. Then the UI rendering code just takes that extra information into account like any other part of the state.

I think the problem in your deletion example is that you had competing representations of what was happening in your system. Apparently you had code that was doing the actual deletion and updating that part of the state immediately, but independently you also had CSS doing the animation, so the visible effects in the UI weren’t synchronised. As you said, you can handle that by making sure the old state you need to render the UI until the animation has finished is preserved somewhere and used for rendering during that time. Maybe that means making a temporary copy and using that for rendering until the animation is done, or for a quick animation, maybe a pragmatic choice is to defer triggering the actual deletion behaviour until the end of the animation.


Both patterns are valid, but do they both follow UI as a function of state?

In the approach where we maintain a temporary copy, should we count the copy as part of the state or should we call UI = f(currentState, oldState)?


I’m not sure there is any fundamental difference. IMHO the defining characteristic of this declarative UI style is that the rendered UI is regenerated from the logical state of the system that comes from somewhere else and not stateful itself.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: