Live data from Hacker News

How UI-driven state increases accidental complexity

evgenii.info

31–40 of 51 posts

Re: How UI-driven state increases accidental complexity

#31

I wish there was more experimentation in the front end landscape. Right now every major framework mixes state, logic and view. It doesn't matter if your state and logic is extracted into hooks, at the end of the day you're still mixing things by importing modules that directly add state and logic to components. I'd love to see a non-trivial app written __without using a single useXXX or class component__. The only th…

I mean... have you heard about redux? This is pretty much what you are describing. If you are looking for UI experimentation js framework are a good resource ;-)

I have used redux in a medium sized app.

The advantage is that there are lots of documentation. Developper can lean on those resources and the result is an architecture that is shared easily among multiple teams.

The disadvantage is that handling asynchronous effects is unclear. Also sometimes adding a flag to manage some subcomponent state require a lot of work. It also clutters your centralized state. It can also be a pain to manage forms this way. And by that I mean managing the state of a form that is in the process of being filled.

Currently my favorite way of designing js apps is a mixed approach. Put all your business code in model classes. Only centralize the state that is important to your application. Sprinkle local states when appropriate. Use hooks & context to inject your model classes in the right components.

Re: How UI-driven state increases accidental complexity

#32
post #17

Controller. You need a controller. Or a smart model, in this simple case. That’s what react/redux has done to you, selling this ‘immutable functional’ flavoured thing. While it is immutable at the programming surface, it is actually a series of complex updates with little to no help from the ‘store’ for convenient access. In pure js, when you want project.tasks, you just: class Project { get tasks() { return db.tasks…

Immutable data makes updates hard and undo easy. It's especially painful in C++.

Mutable data makes mutation easy and undo hard.

I don't know if there's a good solution. The command pattern is based off mutable data, and works, but has lots of boilerplate and implementation (to mutate the document forwards and backwards).

Re: How UI-driven state increases accidental complexity

#33
post #31

I wish there was more experimentation in the front end landscape. Right now every major framework mixes state, logic and view. It doesn't matter if your state and logic is extracted into hooks, at the end of the day you're still mixing things by importing modules that directly add state and logic to components. I'd love to see a non-trivial app written __without using a single useXXX or class component__. The only th…

I mean... have you heard about redux? This is pretty much what you are describing. If you are looking for UI experimentation js framework are a good resource ;-) I have used redux in a medium sized app. The advantage is that there are lots of documentation. Developper can lean on those resources and the result is an architecture that is shared easily among multiple teams. The disadvantage is that handling asynchronou…

Haha I should have mentioned this probably but yes I use redux a lot. The thing is that I suspect that the vast majority of people mix and match redux and local state. And what I have in mind is a strict, 100%, no exceptions separation. Just for experimentation purposes.

> The disadvantage is that handling asynchronous effects is unclear.

To be honest to me this is one of the areas where redux really shines. I've had great success with both redux-saga and redux-observable

Re: How UI-driven state increases accidental complexity

#34
post #9

Earlier quoted context omitted.

There are a lot of approaches you can take with vanilla js, but if your UI follows a component architecture that's modeled as a tree (App > Pages > Widgets), what I'm saying is that you take all of your interactive UI components and separate them into two nodes (a parent and a child). The parent P holds the state and provides callbacks to all descendants, one of which is the child C that you are writing. C is a pure…

So my initial feeling is that it almost seems like you are reimplementing base React. Isn’t this effectively prop drilling, with a bunch of child components lifting state up to the global parent component (so that they all get updates?). Apologies if I’m simplifying it.

Since he's using the context api https://reactjs.org/docs/context.html not exactly.

Imagine you have a React tree with a 100 node depth. You would then need to walk all the way up those 100 nodes, but using the context api you can put in context consumer at the branches of your tree where it makes sense, so instead of walking up 100 nodes you walk up 3 nodes to input into the global context.

Hope that is a reasonable high level explanation of a thing that can be complicated without experience of it.

Re: How UI-driven state increases accidental complexity

#36
post #2

The proposed approach is correct for the proposed use-case. The problem is, lots of UI state isn't handsome, core data like in this example. What about storing the width of a resizable panel? Or the state of a dropdown that can be open or closed? Do you create a dozen little flags in your store for these? Or do you put a flag on each domain object (todo list item, for example) that's not actually relevant to your bus…

> lots of UI state isn't handsome, core data Very true. My interpretation of the article was that it refers to "core data only". If you re-read the article as "How storing core data in a structure that is coupled to the UI increases accidental complexity" then I think you'll find it more agreeable. Personally I throw stuff like popup state into local component state and the global important stuff into redux. Perhaps…

Right- like I said, local state is the natural solution in some cases. But it's not always so clean-cut. Maybe dropdowns were a bad example.

Here's one I've run into: say you have a list of things that can be edited and then when you click "Save" it saves them all to the server. Each of those would need to track a "saving" state to drive its loading spinner. That saving state is transient and UI-specific, and it is tied to each instance of a list item in some sense, but it can't just be local to the list-item component because it needs to be a) set as part of global app logic, and b) read by, for example, the "save" button so it knows when you're allowed to click it again.

There's an entire spectrum of these gray cases and there's no single answer for "how far up to hoist things". You're always making compromises. This is one reason I prefer MobX over Redux, because the latter basically locks you into "global state" and "local state" and doesn't allow for a lot of in-betweens.

Re: How UI-driven state increases accidental complexity

#37
The more obvious solution here would be to use a selector instead of using the state directly. Then your 'view' of the data is not tied to the underlying structure of the state.

Sure, you can denormalize too - but all the more reason to use selectors then to restructure your data.

Re: How UI-driven state increases accidental complexity

#38
Don't model data based on the component hierarchy, but model it in the best possible way. But the "best" approach depends - the same trade-offs as in database schema design applies here.

If most operations require data joins then pick a de-normalized structure. Then most data is often available with a single hash lookup. Though this makes updates difficult and mistakes can and often do lead to inconsistent data.

A normalized data-structure which use ids to refer to related elements makes it easy to add, update, and delete entities; but reads might require multiple joins which in turn makes the code complex.

We can choose between the two by listing out the possible operations and deciding which cases are more frequent. But this is often a moving target in a growing application.

The best approach I've so far found is to design data structures in a way that "invalid states are impossible". I will not link to Yaron Minsky and Richard Feldman's excellent talks on this here. This principle often results in elegant data structures that would've eluded me otherwise.

The second addition that is necessary is to use a statically typed language. Elm, Reason, and PureScript are the only choices in front-end at the moment because of soundness, sum/union types, and exhaustive pattern matching. A typed functional language makes refactoring an easy, mechanical, and reliable process which suddenly makes our code much more malleable and hospitable than before.

Re: How UI-driven state increases accidental complexity

#39
post #24
post #13

Earlier quoted context omitted.

Specifically for dropdowns, you usually want to keep their open / closed position in the UI state explicitly, because you want to close an open dropdown when the user clicks on another dropdown. I frankly think that this complexity is not accidental. It is the real complexity of the UI, which is often not realized. All the little things that need thought and, at least once, an implementation.

The original drop down can close itself when it loses focus. No need go into shared state for this usecase.

Losing focus is itself a stateful change on a UI element caused by another UI element. It's just that the frontend platform like the browser or OS manages that shared state for you.

Re: How UI-driven state increases accidental complexity

#40
post #38

Don't model data based on the component hierarchy, but model it in the best possible way. But the "best" approach depends - the same trade-offs as in database schema design applies here. If most operations require data joins then pick a de-normalized structure. Then most data is often available with a single hash lookup. Though this makes updates difficult and mistakes can and often do lead to inconsistent data. A no…

I believe fable, f# transpiled via babel, would also fit into your list of elm reason and pure script
Post reply on HN