realworld icon indicating copy to clipboard operation
realworld copied to clipboard

Making RealWorld “realer” (2.0?)

Open mindplay-dk opened this issue 5 years ago • 22 comments

This past year I’ve been trying out lots of different front end libraries, and was very happy to discover this project and the community around it.

I’ve been thinking about posting this comment for a while, as I’ve noticed these libraries have a fundamental design difference that RealWorld in its current form doesn’t account for: modularity and reuse.

It seems front end libraries fall into two categories - they either support some kind of architecture for modular reuse (often a stateful component concept) or they don’t.

The Conduit front end, per the current spec, does not present any use-case for any kind of UI control with any internal/accidental state - and while that may be realistic for some simple apps, in my experience, it isn’t enough for larger, more complex apps.

Almost every real-world (duh) app features some kind of recurring UI control that has some kind of state that’s only relevant while the control is on-screen.

Let me give a couple of examples to clarify what I’m talking about.

My favorite example is a drop-down date-picker. It has the selected date state, which is relevant to the application, and therefore typically bound to the application state rather than stored internally in the control instance. But it also has state indicating whether the control is currently open, which month is currently showing, and so on - I refer to this state as accidental, because it is typically of no relevance to the application, and it’s only relevant while the control is visible.

Another example is an auto-complete input. It has the selected value, which is relevant to the application. But also may have an XHR instance, a list of values, the current cursor position in the list, and so on - all of which are accidental, irrelevant to the application, and relevant only while it’s displayed.

It’s not that these kinds of controls can’t be implemented with stateless components/views, they can of course - but the application will need to maintain all this irrelevant state somewhere, and will need to manually keep track of visible controls etc. so that, for example, switching to a different tab in the UI correctly clears the state of, say, a date-picker that is currently open, since the user would likely find it confusing to return to a previous tab and find a date-picker (that they didn’t just click on) already open.

Implementing all this state management and wiring can sometimes be difficult in front-end libraries that don’t provide any kind of concept for modular reuse - sometimes near impossible.

Almost every major React, Vue or Angular project uses either third-party or first-party components/controls that have accidental state, so it seems pretty relevant, I think.

Something to think about for v2.0 maybe?

mindplay-dk avatar Sep 06 '18 18:09 mindplay-dk

Hi @mindplay-dk 😄

So is your thought that we should break down the front end spec into individual components with some guidance on component state management?

I think that works for most frameworks and could be super useful as a learning tool when digging into a particular framework. It could certainly make realworld more realworld (see what I did there 😂)

The downside in my opinion is the the learning curve that adds for someone who is just getting into looking at a framework. Typically, at least in my experience, when you start learning a framework you are learning the api's, conventions, and structure; then as you get comfortable with those things you begin to really leverage component reuse and more efficient state management.

I'd be a little hesitant to make those things bare minimum but I'm for sure open to keeping the discussion open and seeing how we make components and state management more of a focus on the next spec version.

@anishkny?

Cameron-C-Chapman avatar Sep 11 '18 04:09 Cameron-C-Chapman

So is your thought that we should break down the front end spec into individual components with some guidance on component state management?

Not at all :-)

I don't think we can/should provide any general guidance on component state management, or anything else that resembles architecture - because this is where the frameworks differ. The spec shouldn't dictate anything other than the expected end result - each implementation should leverage whatever concepts are available in the selected frameworks/libraries. That's sort of the whole point, I think? :-)

What I'm pointing out is the fact that, for example, if you were going to implement the Conduit front-end with, say, React, you could do so entirely using functional components - there's no recurring control in the UI that substantially benefits from using the stateful component abstraction.

In other words, we're not leveraging or exercising the concepts that make React interesting.

There are several ultra-lightweight UI frameworks/libraries of 1-2 KB that only support the equivalent of stateless functional component - any of these can be used to implement Conduit's front-end without problems.

But that's unrealistic - as said:

Almost every real-world (duh) app features some kind of recurring UI control that has some kind of state that’s only relevant while the control is on-screen.

Conduit's UI doesn't have any controls in that category.

It has recurring portions of UI, but none of those portions are "controls" in the sense that they have individual state tied to their individual life-cycle.

I'm suggesting we need to think of an additional feature/case that calls for e.g. stateful components in React, or other features in other libraries using whatever concepts those offer for reusable controls. (some of them don't offer anything, and that will lead to interesting results not currently seen in implementations - which will either demonstrate limitations/weaknesses or alternative patterns that illustrate why those concepts aren't necessary when using those libraries.)

mindplay-dk avatar Sep 11 '18 19:09 mindplay-dk

For me how state/route is implemented should be for each example. I do wish that there was a forgot password built into all the examples, and a few examples of uploading files for each framework which would save many people time.

lastlink avatar Sep 11 '18 22:09 lastlink

@lastlink I think those issues are interesting, but unrelated and belong in a separate discussion.

mindplay-dk avatar Sep 12 '18 15:09 mindplay-dk

There is still, to my knowledge, no demo project suite out there that requires control state.

It's a bit frustrating - so many new frameworks don't support this (to any practical extend) and a lot of developers aren't going to realize that before it's too late, as many rely on this demo suite to evaluate whether a given framework "works".

I believe most projects will eventually need control state - a lot of these frameworks wouldn't support it, and if you built a large project by the time you realize that, you'll have to either make an expensive switch to a different framework, or (worse) hand-write code to manage arrays of control state as part of your application state, manually manage a control state life cycle, and so on.

I wish you would raise the bar on these frameworks, so we can see how they really hold up in the real world. I think that's the purpose of this project?

mindplay-dk avatar Apr 16 '19 05:04 mindplay-dk

@mindplay-dk Could you maybe try to create an upgrade to the current spec and maybe create an example for one of the implementations with something that you think would add what you are talking about?

Alonski avatar Apr 16 '19 06:04 Alonski

@Alonski we would need to think of a new feature that actually requires a component that has some sort of control state and occurs more than once.

Maybe an advanced search form with start and end date pickers? The main problem with that idea is that calendar logic is quite complex and doesn't really help demonstrate anything relevant in terms of comparing UI libraries. You've picked a source of reference (Medium) that by design is very minimalist in terms of UI and avoids basically anything that requires a stateful control.

Plenty of admin forms, for example, would require various stateful controls - maybe we could think of a back-end/admin screen of some sort that has a more complex form?

I'll keep thinking about it, but it's actually difficult to think of something for this kind of app.

mindplay-dk avatar Apr 18 '19 10:04 mindplay-dk

Also most date pickers are wrappers around existing ones. Needs to be something that you can't just npm install a fix for.

Alonski avatar Apr 18 '19 19:04 Alonski

I do have a small date picker that I keep porting to different frameworks for testing - it's only around 50 LOC for the calendar logic...

https://gist.github.com/mindplay-dk/d85fbf5427d5e4436fadc70ce46d9ac2

I would prefer finding a feature that doesn't require everyone to copy/paste 50 lines of logic though. It might have bugs, never really used or tested ;-)

mindplay-dk avatar Apr 19 '19 07:04 mindplay-dk

I was most surprised when a forgot password wasn't included in the frontends.

quantuminformation avatar Jun 26 '19 08:06 quantuminformation

I was most surprised when a forgot password wasn't included in the frontends.

@QuantumInformation that doesn't really present a new challenge to the frameworks though - just more of the same. The point of this thread is, there's an entire class of components with instance-specific state that isn't covered by any of the current cases - and this appears to be something that significantly sets frameworks apart.

mindplay-dk avatar Jul 02 '19 12:07 mindplay-dk

@mindplay-dk

There is still, to my knowledge, no demo project suite out there that requires control state.

If you think about the TodoMVC as a control part of a larger application, rather than an application in its own right, then you have one.

so many new frameworks don't support this (to any practical extend) and a lot of developers aren't going to realize that before it's too late, as many rely on this demo suite to evaluate whether a given framework "works".

Folks have been writing web applications with jQuery and direct DOM manipulation and plenty of controls without a hitch. I don't think the situation would dramatically change with any framework, small or large. It is not like you need a framework to manage state of any kind, worse case you do it yourself (plain vanilla-JS, web components etc.) or with an extra library -- there are just plenty of options. For instance, there are standalone implementation of Hooks (a-la React Hooks) that can be used in any framework. My point is you never reach a point where it is too late. You just go about things differently.

I do agree that scope is something that sets frameworks apart. Focusing only on the UI is certainly a different value proposition as doing scheduling, suspense, state management, effects and what not in the same framework.

It is also true that the more complex the application, the more resorting to a complex framework is an interesting choice -- that is actually why we have frameworks: to achieve productivity through reuse, and higher readability and maintainability through consistent patterns. Looking at the picture only through the lens of performance (as does this benchmark), leads to focused UI libraries and plain JS to be sure winners.

On the subject of RealWorld 2, I totally support that, but I would not necessarily focus on what may seem on first look as artificial technical details. I would just try to find, well, a real-world app, that people can understand just by the title description. What comes to mind are booking systems (restaurants, plane tickets) or generally e-commerce systems. Those applications generally feature a stateful multi-step process with plenty of forms, and which can be difficult to implement properly and concisely.

As a final world, let's not forget the goal, which is to compare the implementation of the same application with multiple frameworks on a credible application. If you are by design choosing features of this application that are best handled in large frameworks, you are biasing the effort. If you are choosing features of this application to make it simple, you are also biasing the effort towards specialized UI libraries. I would say find a real application, in the sense of a real, consumer site not in the sense of complex or else. Then if that app needs a date picker, then that's that.

brucou avatar Aug 07 '19 15:08 brucou

there are standalone implementation of Hooks (a-la React Hooks) that can be used in any framework

This gives you state - but it doesn't somehow give you update boundaries pertaining to that state.

Some of the simpler libraries will let you render and update JSX - if you use this approach with one of those libraries, any state update will necessarily cause a complete render/diff of the entire UI.

If you are by design choosing features of this application that are best handled in large frameworks, you are biasing the effort.

I don't think I'm doing that?

Even a tiny app like, say, a cookie consent banner, might have a few tabs and a custom drop-down - if you're going to package and deploy that, you most likely want a very small framework you can embed.

Of course you can render and diff the entire application top-down on every input keystroke - and of course you can manually create and manage the life-cycles and every detail of every control by modeling everything as application state. But performance will be pretty awful, and the resulting code will be a jumbled mess of application/control state and instance management.

I've personally found that this requirement can surface for even the simplest of use-cases - so I don't think this isn't about "large or small" at all, it's about handling a fundamental UI requirement in a sensible manner.

For example, this tiny 512 byte library does support control state. That's small, right? So this isn't just handled by "large frameworks".

And if you still think this requirement is biased towards a particular approach, or a particular type of abstraction, I disagree - this is simply another UI case demonstrating a real-world problem.

It doesn't dictate a specific solution, or even a framework solution. If the person implementing the app can demonstrate a simpler, better, faster or cleaner approach to solving the problem with a particular library, whether the library implements it as a feature or prescribes a particular pattern, I'd like to see how they compare.

My suspicion is that, yes, some libraries will be able to handle control state more/less elegantly than others - but that's pretty much the case for every challenge presented by RealWorld, I think: the whole point of it is to compare implementations using the libraries and the patterns they prescribe, to see how they fare in different situations.

In that sense, the whole project is opinionated or biased towards solving certain problems.

I don't see how that's a bad thing, and I don't believe this requirement is exotic or rare at all.

mindplay-dk avatar Aug 08 '19 14:08 mindplay-dk

there are standalone implementation of Hooks (a-la React Hooks) that can be used in any framework

This gives you state - but it doesn't somehow give you update boundaries pertaining to that state.

I am not sure what you mean by that. TNG-hooks for instance lets a component access and update state, and that state would be only accessible from that component (is that what you mean by boundary?). The same state management abilities can be even more easily achieved with a simple closure (whose scope you can adjust). Also, using web components allow to turn any UI library which purely concerns itself with updating the DOM into a library which offers stateful components. For instance, from lit-html you get lit-element.

For example, this tiny 512 byte library does support control state. That's small, right? So this isn't just handled by "large frameworks".

That is a good find. I had not hear about it. I am actually writing the Realworld app as Kingly state machine, and I decided to use Svelte. What is interesting with Kingly is that you can switch your UI without changing the machine (the machine encodes the specs, so if the specs do not change, the machine does not). I already have demonstrated a movie search app in 7 different UI libraries, with the exact same machine and where the UI library only ever does rendering. So I could use dot.dom for my second implementation of the Realworld app. I will see how it goes, but if the process is smooth, I might just have the Realworld app in 14 different UI libraries.

In that sense, the whole project is opinionated or biased towards solving certain problems.

I agree it is kind of impossible to eliminate bias completely. My point is that I am more interested in the real part of real app that in the technical challenges of the app, so the primary driver for picking up the next app would be for it to be realistic. In other words, I am interested in the challenges of a realistic app, whatever they turn out to be, rather than in creating a ficticious app with specific pre-determined challenges.

In that sense, the current RealWorld goes half the way, it is indeed realistic (it is a medium clone after all) but not enough -- it is not the prettiest, the UX can be improved (reloading stuff at every click, loading states could be better handled), error flows seems to be absent, etc. Those little things, which are like, say, 5% of the specs may be 20% of the troubles when writing an app. So I am interested in realistic specifications like the consumer apps we use every day have.

brucou avatar Aug 08 '19 16:08 brucou

This gives you state - but it doesn't somehow give you update boundaries pertaining to that state.

I am not sure what you mean by that. TNG-hooks for instance lets a component access and update state, and that state would be only accessible from that component

Yes, TNG-hooks lets a component access and update state while it is rendering - by render boundaries, I mean, control of where rendering begins and ends: the scope of an update.

You can tie-in TNG with, for example, this tiny library - and this will give you stateful components during updates, but it won't somehow imbue it with any concept of render boundaries for those components... So, anytime a single state value changes anywhere in the application, you will have to update the entire UI top-down.

If a library doesn't have the ability to update individual components, TNG can't fix that - and practically any library that has the ability to update individual components also has some kind of component instance, implying some sort of component life-cycle, and most likely some means of associating state with those instances.

if the process is smooth, I might just have the Realworld app in 14 different UI libraries.

Wow! I wish I had that kind of energy :-)

I am interested in the challenges of a realistic app, whatever they turn out to be, rather than in creating a ficticious app with specific pre-determined challenges.

I don't think there's anything ficticious about control state - I personally see this need coming up even in very small apps much simpler than RealWorld. Even some of the primitive, standard, native browser elements, such as <select>, have control state.

Anyhow, we can agree to disagree on this point :-)

mindplay-dk avatar Aug 10 '19 10:08 mindplay-dk

Alright I think I understand what you mean. Without a component concept, you cannot have a component granularity basically. But you can always build your own component concept around the minimal UI library if you need to. Or you can memoize some of the vDOM generating functions so they don't recompute. Or you can just accept the virtual DOM cost. I agree that it may be an hassle and this may come with tricky edge cases that a componentized UI library has already solved for its users.For me what you mention is more of an optimization problem (minimizing unnecessary vDOM computations) that is common to all vDOM-based UI libraries, than a breaking point.

Thinking about it, I believe though that template-based UI libraries would probably not have that problem, as the UI library can deduce from the template the exact DOM operation to perform when a dependent state variable changes. So you could have a single template without any component, and you would only see surgical updates of the DOM. That is how Svelte works, but also probably Vue.

About Realword app and the 14 implementation of it, this is because I already did the same app with 7 UI libs, so it is double that because a man needs to challenge himself. It is not that time consuming, given that with the Kingly state machine library, the UI library only ever does rendering and that is pretty much the same in every library so the process is smooth. There are always some tiny differences in syntax but that is generally quickly solved. But it surely is a repetitive process.

On 8/10/19, Rasmus Schultz [email protected] wrote:

This gives you state - but it doesn't somehow give you update boundaries pertaining to that state.

I am not sure what you mean by that. TNG-hooks for instance lets a component access and update state, and that state would be only accessible from that component

Yes, TNG-hooks lets a component access and update state while it is rendering - by render boundaries, I mean, control of where rendering begins and ends: the scope of an update.

You can tie-in TNG with, for example, this tiny library - and this will give you stateful components during updates, but it won't somehow imbue it with any concept of render boundaries for those components... So, anytime a single state value changes anywhere in the application, you will have to update the entire UI top-down.

If a library doesn't have the ability to update individual components, TNG can't fix that - and practically any library that has the ability to update individual components also has some kind of component instance, implying some sort of component life-cycle, and most likely some means of associating state with those instances.

if the process is smooth, I might just have the Realworld app in 14 different UI libraries.

Wow! I wish I had that kind of energy :-)

I am interested in the challenges of a realistic app, whatever they turn out to be, rather than in creating a ficticious app with specific pre-determined challenges.

I don't think there's anything ficticious about control state - I personally see this need coming up even in very small apps much simpler than RealWorld. Even some of the primitive, standard, native browser elements, such as <select>, have control state.

Anyhow, we can agree to disagree on this point :-)

-- You are receiving this because you commented. Reply to this email directly or view it on GitHub: https://github.com/gothinkster/realworld/issues/262#issuecomment-520136166

--

brucou avatar Aug 10 '19 12:08 brucou

you can always build your own component concept around the minimal UI library if you need to

Of course, you can always build your own library on top of another library.

But the point of RealWorld, I think, is to show what you can do with a given library and appropriate patterns?

If I had to build my own component abstraction with diffing and life-cycle management and all, on top of another library, that new library becomes a candidate for a separate case-study and implementation.

For me what you mention is more of an optimization problem (minimizing unnecessary vDOM computations) that is common to all vDOM-based UI libraries, than a breaking point.

Up to a point, sure - it depends on the overall complexity of the UI.

If it's large enough, rendering everything top-down, at every keystroke in every input, becomes untenable - you can't reasonably ship an app that lags and bogs down the UI thread at every keystroke.

Real world projects often have a screen with, for example, an information-dense table and an edit-form being visible at the same time. Nobody wants to start building an entire project with a chosen library, just to discover down the line that it's complete infeasible in practice for a use-case that emerges later.

The assurance I personally hope to get from something like RealWorld, is that a given library and it's associated patterns work, and keep working, in the real world - otherwise, I don't know what the point is. If you're not concerned about scaling to the complexities of real UI, TodoMVC would likely suffice.

mindplay-dk avatar Aug 19 '19 07:08 mindplay-dk

Alright, so I guess that if my point is not clear, then indeed example of implementations around miscellaneous UI lib will do a better job to dispel any doubts.

On 8/19/19, Rasmus Schultz [email protected] wrote:

you can always build your own component concept around the minimal UI library if you need to

Of course, you can always build your own library on top of another library.

But the point of RealWorld, I think, is to show what you can do with a given library and appropriate patterns?

If I had to build my own component abstraction with diffing and life-cycle management and all, on top of another library, that new library becomes a candidate for a separate case-study and implementation.

For me what you mention is more of an optimization problem (minimizing unnecessary vDOM computations) that is common to all vDOM-based UI libraries, than a breaking point.

Up to a point, sure - it depends on the overall complexity of the UI.

If it's large enough, rendering everything top-down, at every keystroke in every input, becomes untenable - you can't reasonably ship an app that lags and bogs down the UI thread at every keystroke.

Real world projects often have a screen with, for example, an information-dense table and an edit-form being visible at the same time. Nobody wants to start building an entire project with a chosen library, just to discover down the line that it's complete infeasible in practice for a use-case that emerges later.

The assurance I personally hope to get from something like RealWorld, is that a given library and it's associated patterns work, and keep working, in the real world - otherwise, I don't know what the point is. If you're not concerned about scaling to the complexities of real UI, TodoMVC would likely suffice.

-- You are receiving this because you commented. Reply to this email directly or view it on GitHub: https://github.com/gothinkster/realworld/issues/262#issuecomment-522449111

--

brucou avatar Aug 19 '19 15:08 brucou

I would also like to see and work on v2. The one big thing that I think is missing in v1 is indeed more complex forms with validations. I was looking at the react and the apprun implementations and they don't seem to do client side validations on the login and registration forms. crizmaz-mvc does. Also in the crizmas-mvc example I'm presenting some pending state as well, for instance the 'favorite' heart button is disabled while the request is pending. The react and apprun examples don't implement something like that. I think it would be good for the spec to have such ux details as well.

Regarding forms, a nice to have scenario would be one that includes a wizard with a few pages (such that parts of the wizard are not rendered) and with lists of rows of fields. Perhaps adding and removing rows dynamically. It would be nice to have expandable/collapsible sections in order to challenge libraries that keep state in the components so that the validation state could be lost when the section is collapsed. It would be interesting to allow jumping from one step of the wizard to another that has already been completed and maybe allow to enter some invalid date and then allow jumping to the last page where the user submits. In that case the submit shouldn't work and maybe redirect to the user to the step where the field had an invalid value. Even more interesting would be to have to reuse that in multiple areas of the application.

Also another interesting aspect is presenting the same data/state in different parts of the application without losing that data/state when the context changes, so that we can see how the separation of concerns is addressed. All in all, we should have more complexity.

I think the focus shouldn't be on more sophisticated components such as datepickers or typeaheads because frameworks typically don't provide them. If 3rd party components are used, the size of the bundle might look different depending on the library. Or, if needed then at least have some not too complicated implementation that can be ported to each framework (meaning, not too much tied to a specific framework).

raulsebastianmihaila avatar Sep 13 '19 18:09 raulsebastianmihaila

It would be nice to have expandable/collapsible sections in order to challenge libraries that keep state in the components so that the validation state could be lost when the section is collapsed.

This is somewhat related to the problem I've been trying to define.

The very easy work-around is to just use CSS to control visibility, however - the problem I've been trying to describe is a bit trickier in that regard, since you'd have to destroy the component (or manually manage instances/states) to make it lose it's state.

I think the focus shouldn't be on more sophisticated components such as datepickers or typeaheads because frameworks typically don't provide them.

I would prefer to find a simpler use-case - a date-picker has a lot of irrelevant details regarding date logic.

A simple custom drop-down provides the same challenge. If instances of these were located on tabs, like you describe, the CSS work-around doesn't get you around this easily, since merely hiding the tab with the drop-down on it doesn't automatically cause the drop-down instance/state to reset.

mindplay-dk avatar Sep 14 '19 11:09 mindplay-dk

The very easy work-around is to just use CSS to control visibility, however - the problem I've been trying to describe is a bit trickier in that regard, since you'd have to destroy the component (or manually manage instances/states) to make it lose it's state.

I think the safest scenario is the wizard scenario where each step is on a separate page (meaning has a different URL path). It's less likely then that the form state would be held in the component. But it shouldn't be forbidden, at the end of the day the project is about comparing approaches. Some of the steps should have state/data from the server and it should be required in the UX spec that that data is loaded only when the user gets to that step. Otherwise, it's possible that the user cancels the flow and so fetching that data from the beginning is not needed. As far as I know Vue allows you to reuse a component instance so that its state is not lost. But at some point I guess you would also need to reset the state so that after the flow is completed, if the user enters the flow again, they don't see the old state. Once the flow is started, it could be paused and later resumed. Once the user goes on a different page that is not part of the wizard a banner should be displayed on the page with some state of the wizard or some derived state, like the sum of something and it should also provide the ability of reentering the flow. The banner should also have the final submit button such that when the user clicks it, if there are steps with invalid fields (such as required fields with no value) the user is redirected to the first step with invalid fields. But also important: multiple rows with fields, with the ability of adding and removing rows.

raulsebastianmihaila avatar Sep 14 '19 12:09 raulsebastianmihaila

Regarding forms and validation, here's my thoughts already, at least it should have some server side/client-side checks (before submitting) on the users email better than what we have.

https://github.com/sveltejs/realworld/issues/34 https://github.com/gothinkster/gcp-datastore-cloud-functions-realworld-example-app/issues/45

quantuminformation avatar Oct 01 '19 16:10 quantuminformation