I'm one of those "old dogs" who don't quite get the advantage of using React in the majority of the cases it's used. This article didn't really help...
> What makes these frontends complex?
State management. The frontend “knows” a lot of things, and these things interact with each other in non-trivial ways [...] Example: dark mode support. For example, say your app has a dark mode. All your rendered components must know what theme is on, so they can render the UI in the right color.
Back in my day.... you'd slap a class on the <body> tag, and you'd have all you'd need for your CSS to style the rest of the page accordingly. How is using Redux an improvement here?
None of the examples of "complex" front-ends would be at all difficult in an old-school "just re-render the page" setup using something like pjax or turbolinks. And in my experience (seeing web pages with > 1MB js files) it'd be faster/more responsive for the end-user too.
React and Redux are for apps. If you can do it with HTML/CSS, you absolutely should.
React/Redux are a replacement for what people used to do with jquery or plain JS. They're not for display; they're for complex interaction of the kind that used to require a native user interface (Java Swing, Gtk, Win32, etc). Javascript is a mess, and the DOM is a mess; React makes that slightly less awful. (Redux is an extension to React to help manage global state; again, it's compensating for just how bad Javascript is.)
Bonus: React encourages the use of JSX, a language that makes a lot of behavior look as if you were writing it directly in HTML. You don't need React to use JSX, and you don't need JSX to use React, but they do dovetail nicely.
You can write in plain JS/jquery if you want. Plenty of people do. React is a framework to make that a bit easier; that's all.
I think what GP was getting at is that there's a healthy amount of "if all you have is a hammer" mentality going on w/ React/Redux. The tragedy of commons here is that you _could_ conceivably just slap a class at the root of your React tree to implement dark mode, but if one is using something like styled-components, there's a good chance that the library will get in the way of the style cascading semantics that come for free w/ the browser. And ultimately, the end-user is the one paying the price for all the extra code needed to make a "complexity-friendly" implementation of dark mode work.
Another observation that I've been finding interesting about "modern frontend development" is that despite all the arguments about how React makes DOM manipulation easier, people still prefer to defer to libraries to do any non-trivial amount of DOM manipulation rather than actually use pure React to do it. This is exactly the same modus operandi as when jQuery was "modern frontend development" ("it makes it easier to work w/ DOM", plus a ton of opaque jquery-* libs)
I'm not sure what point you're trying to make here.
Both of those examples are _specifically_ about using React to do the actual DOM manipulation.
React-Bootstrap is a set of React components that know how to generate the correct HTML structure and classnames to get the matching Bootstrap styles. As part of that, I believe some of the jQuery-centric logic in Bootstrap has been implemented in just React.
For drag and drop especially, you're having to approach the problem from a different mental model. You can't just `$(".my-list").sortable()`. React wants you to describe your UI based on _state_ rather than doing raw DOM manipulation, so libraries like react-dnd and react-beautiful-dnd do the work to tie together drag events with the underlying logic to drive React re-rendering.
But in both these cases, you're not "using other libraries to do DOM manipulation". React's doing the work. These libraries are just helping tell React what the DOM should be.
This is why I was reluctant to give examples (because as soon I did, someone was bound to say something along the lines of "well these leverage react in a very react specific way so of course it makes sense they're react things")
The thing is that we could s/react/jquery/ and end up with a more-or-less believable argument for why "jquery is needed for complex apps": You can't just `draggable=true`. jQuery wants you to describe your UI in terms of a plugin's options object rather than doing raw DOM manipulation; libraries like jquery-ui do the work to tie together drag events with underlying logic (e.g. snap) to drive jQuery re-rendering. You're not "using other libraries to do DOM manipulation"; jQuery's doing the work. Etc.
Of course a library on top of react/jquery/whatever will generally be written in a way that is idiomatic for code written on top of its respective base framework. For library writers like you and me, it might even make sense to argue that React rendering model is nicer for writing libraries on top of than jQuery. But that's a developer-oriented mindset, which, as the_gastropod had alluded to, it's very often at odds w/ a user-oriented mindset.
The "old beard" approach slices through all the abstraction layers and simply points out: huh, you don't need context and css-in-js and themes to do dark mode, plain CSS fits the bill. And when one resets assumptions enough times and start noticing more often than not that "complex" projects turn out to be glorified CRUD apps, one might indeed start to wonder if the abstractions are really as fitting for the task at hand as they should be.
I'm not sure it's a bad thing - but using a library for DOM manipulation on top of it diminishes the argument that React's advantage is to make DOM manipulation easy.
Having used react and modern web components to build webpages. I will agree with the first comment which says what is made so hard by react/redux can be easily done if the pages were rendered on server and delivered to client with progressive smaller bits of JavaScript for dynamic interaction.
If state management is so critical to running a react app than I feel Elm/ClojureScript is a better choice than react to build web apps. But I doubt they will be as popular given network effect.
Now everyone went on a bandwagon that react/angular/SPA/progressive apps is the only future and plain HTML/CSS/JS mix on server side with specific language templates is dead. I am sure we will again go full circle back as you can see now with SSR (server side rendering) is considered as a norm for performant react or angular apps. SSR combined with complexity of react/redux unnecessarily makes everything complicated (worse than even complex). Instead of doing HTML templates in nicer language on server side now people are forced with half baked solution on client side like JSX or angular templates etc.
A developer not just need to learn intricacies of HTML/CSS/JS, but then API's of framework like react, combine it with state management framework, combine it with client side templates, combine it with build tools like gulp, webpack, combine it with NPM, Yarn, combine it with babel compiler, combine it with many testing frameworks. The whole eco-system is very complicated (not just complex).
This is one of the reason for emergence of compiler based web application development language and frameworks like Svelte, Elm, ClojureScript, PureScripts. They will still be not popular because there is a network effect of jobs available. So inferior tools for user interface like react/angular will continue to thrive.
> Now everyone went on a bandwagon that react/angular/SPA/progressive apps is the only future and plain HTML/CSS/JS mix on server side with specific language templates is dead
Well it is not dead, but with the rise of Webapps (not Websites!) come use-cases you can only handle with SPAs. One major example is the new youtube feature, where the video playback is not interrupted when you click on a link somewhere (the video stays at the right bottom corner of the page and keeps plaing). No way to do this with SSR.
Other examples include staying in fullscreen mode (think automatic playback of video playlists) - with SSR, every video change would trigger a page reload and thus require user interaction to go back to fullscreen.
SPAs are not required for Websites, but for Webapps there are a lot of cases where SSR is not possible or would result in poor UX.
> The tragedy of commons here is that you _could_ conceivably just slap a class at the root of your React tree to implement dark mode, but if one is using something like styled-components, there's a good chance that the library will get in the way of the style cascading semantics that come for free w/ the browser.
A decent dark mode is going to take a lot than a class at the root in any case (a class at the root makes a good toggle, sure, but you have to do all the styling if particular elements that that toggle applies.)
If you have handrolled CSS for your light theme and want to add a dark theme toggle, hand rolling CSS for it and toggling with a class at the root is pretty idiomatic React.
If you've chosen a styling/theming library to solve other problems, or incidentally to doing so as might have happened if you chose Material-UI, then, sure, a class at the root might not work well, and/or you might have a much easier solution (not easier than the class at root but easier than the rest of the work of implementing a dark theme) for adding a toggleable dark theme. Like in Material-UI, where you just tell the theming solution you’d like the computed dark version of a theme.
I actually write a good bit of React professionally. I'm familiar. I just have yet to see a very compelling case. And the excuse is almost always this nebulous "rich/complex interaction". And.... I just rarely see any good examples of this. The huge majority of apps (web or otherwise) are not video games. They're forms that submit stuff to a server and get a response. There's sometimes a dropdown here or there.... and that's it.
I see this sentiment on hacker news a lot, and I honestly dont get it. A list of functionality I've implemented that requires (or is made easier by) JavaScript:
Client side validation, error messages, autocomplete, dynamically picking/removing/auto filling fields based on user input, forms that need to know user answers to previous forms, smart tables, update of data pushed from server, sharing markup/functionality between pages, removing markup when no longer needed, adding markup when relevant, etc.
Of course, all of the above functionality can (and has been) implemented in jQuery, or vanilla js. But using a framework makes it easier to structure and edit the project.
The server still needs to validate, no? I've never seen a form that really benefitted from client-side validation. As long as required fields are clearly marked, I don't really understand the value here.
> error messages
Hmm? What about them needs React?
> update of data pushed from server
You can poll very cheaply. And even Websockets are pretty simple. Check out Phoenix LiveView or Rails Action Cable for decent examples of trivial solutions to this.
> sharing markup/functionality between pages
Even Apache server-side includes let you do this. We're not talking about hand-rolling plain HTML files in MS notepad (or even going wholly js-free). We're talking about React being overkill in a lot of places.
> I've never seen a form that really benefitted from client-side validation. As long as required fields are clearly marked, I don't really understand the value here.
Required fields are typically not the only type of validation required by a form. I think a designer would see the value of client-side validation more easily than a developer. It's not to ensure correctness - it's to give the user quick and actionable feedback.
> You can poll very cheaply. And even Websockets are pretty simple. Check out Phoenix LiveView or Rails Action Cable for decent examples of trivial solutions to this.
React isn't helping you poll - it's helping you structure your application in a way that ensures all components receive the updated data.
> it's to give the user quick and actionable feedback.
Right.. But I think often times the difference is pretty overblown. Clicking "save" and getting that feedback in ~100ms is not, in my estimation, worth the massive extra overhead of using a front-end framework, duplicating your validation requirements, etc. If you're google or facebook or youtube or are otherwise printing money, go for it. But for the 99% of web apps out there, I think this is a bad trade.
> React isn't helping you poll - it's helping you structure your application in a way that ensures all components receive the updated data
Right.. But so is just re-loading (most of) the page from the server. Again, e.g., Phoenix Live View or Action Cable style.
> Clicking "save" and getting that feedback in ~100ms is not, in my estimation, worth the massive extra overhead of using a front-end framework, duplicating your validation requirements, etc.
Is it worth spending more developer time (think $$) on optimizing things for imaginary savings on initial page load time?
> Right.. But so is just re-loading (most of) the page from the server. Again, e.g., Phoenix Live View or Action Cable style.
How is that less complex than a client side application? You are essentially advocating for splitting the logic between server and client side. Of course that is possible and people do that all the time for various reasons. Does not mean that's the best way of doing things.
I think you are missing the elephant in the room. Emergence of the front end development frameworks like React was caused by ever increasing complexity of the front end applications. Which, in turn, is driven by customer demand. Customers pay for features, not for code quality, and churning out features is much easier and quicker using React vs. e.g. jQuery.
Lamenting front end application overhead is the same as lamenting using high level languages for back end services. Everybody knows that Real Programmers wrote Real Programs in assembler back in the olden days!
I'm with you on the fact that React is overused, but:
> in ~100ms is not, in my estimation, worth the massive extra overhead of using a front-end framework
The phenomenon of change blindness means a 100ms "blank screen" can be the difference between the user recognizing the fact that they've made an error and getting frustrated because they can't tell where the error is. The 100ms reload means if you don't want that to happen, you have to be much more careful about how you design your errors.
It's an easier design problem if the user sees the error appear immediately where their attention is already focused.
> Right.. But I think often times the difference is pretty overblown. Clicking "save" and getting that feedback in ~100ms is not
Let's say the form has 5 fields, and I as the user make an error in the first field.
Why do I have to finish filling out the entire form to hit "save", to discover I made an error in the first field? That's not a difference of ~100ms, that's a difference of several seconds (or tens of seconds for a medium-complexity form).
>Why do I have to finish filling out the entire form to hit "save", to discover I made an error in the first field?
Because more validation will usually happen on the server side anyway and finding out when you hit save creates less interruptions to your flow. There's no tabbing back to re-enter a field, there's no thinking it's fine only for the server side validation to reject it and there's no nagging when I skip a field to come back to it later, there's no UI jumping around when the error is shown. Client side validation takes a stateless form and interrupts me with it's stateful validation. At best it's an interruption, all too often there are silly things like not letting you tab to the next field or warning you that the second password you haven't yet entered doesn't match (and then later telling you about other problems when the backend does the validation).
Not to mention it's easier, whether you agree or not with "developers are expensive so performance doesn't matter" in a world where this is often said I'd expect more server side only validation because client side validation is duplicating the work.
I don't think client side validation is necessarily bad, but most implementations get in my way more than the server side equivalent.
> all too often there are silly things like not letting you tab to the next field
Well, that's a criminal use of client-side validation, but it shouldn't condemn all client-side validation.
> Not to mention it's easier, whether you agree or not with "developers are expensive so performance doesn't matter" in a world where this is often said I'd expect more server side only validation because client side validation is duplicating the work.
That's fair, but as others have noted if you're in a world where a polished user-experience really makes a difference or is a competitive advantage I'd argue that well executed client-side validation can get you to a UX-quality bar that no amount of server-side validation can reach (edit to add: server-side validation is still, of course, absolutely required).
Whether that's truly important for a given business or product can only be argued on a case-by-case basis. I think it's definitely a colorable argument that there's some overuse of client-side validation relative to product goals, but I think there are definitely places where client-side validation is hugely impactful.
Again, all of this assumes that the client-side validation is implemented at a high quality bar. Of course bad client-side validation is not useful.
There is definitely a happy middle ground. Feedback too soon can be jarring, but waiting till the end of a form can be very frustrating, especially if there are many fields and you have to reparse to find the error. I think you need both to have a good experience.
I just think that, if you're aiming for the highest UX-bar, you're going to need client-side validation at some point. For any mildly complex form, server-side validation will usually be far past the sweet-spot you're referring to.
Now, as others have mentioned, it might be questionable whether every web-app and website should be targeting the highest UX-bar.
By the time the user presses the save button more than 100ms have passed and the user probably has filled in 10 other fields. You can avoid this frustration with a little bit of Javascript.
> The server still needs to validate, no? I've never seen a form that really benefitted from client-side validation. As long as required fields are clearly marked, I don't really understand the value here.
Any non trivial form is going to have non trivial validation. Validity for some fields will depend on other fields, the validity for a single field could have complex rules, and forms can be lengthy. It helps the user to get validity feedback without sending information back and forth from the server.
> Hmm? What about them needs React?
> You can poll very cheaply. And even Websockets are pretty simple. Check out Phoenix LiveView or Rails Action Cable for decent examples of trivial solutions to this.
Like I said in my OP, none of the things I listed require React, some dont even require JS. But having worked in large codebases of jQuery and large codebases of framework code, frameworks scale up better. LiveView is cool tech though.
> Even Apache server-side includes let you do this. We're not talking about hand-rolling plain HTML files in MS notepad (or even going wholly js-free). We're talking about React being overkill in a lot of places.
If you need to share markup with dynamic values, you need a full programming language. I'm aware that there are many solutions for sharing markup, but there are fewer solutions for sharing markup with dynamic behavior. Again, a framework scales up better.
To be clear, I'm not suggesting React for everything. But the GP comment was suggesting it was good for nothing, so I provided counter examples.
Client input should never be trusted. Server needs to validate, but that doesn't mean client shouldn't validate as well.
> I've never seen a form that really benefitted from client-side validation. As long as required fields are clearly marked, I don't really understand the value here.
Client side validation is not only about required fields, it's much more than that. Have you ever used a web store where you need to enter your shipping address, credit card info, etc? Address, zip code (or worse, UK style postal code), phone number, email, credit card number - all that needs to be validated.
Sure the server can (and should) validate that. The problem then is how to let the user know they've made a mistake and how to fix it. Accessibility issues aside, users need to be told that they've made a mistake as soon as it is made, otherwise there is a chance they won't find the error message, and simply give up trying. Lost sales is the reason client side validation was invented.
> Hmm? What about them needs React?
If you don't want to submit a form with full page reload, you need to make an Ajax call with form info. When response comes back with a list of errors, you need to walk through the fields on the page and mark the relevant ones as invalid, with corresponding error messages displayed _close_ to the input fields. Of course that is doable in plain JavaScript, and doing that seems trivial for one form. Then you have to duplicate that code for another form, or abstract it in a library... Congrats, you're on your way to reinventing React. Or worse yet, Angular.
> You can poll very cheaply. And even Websockets are pretty simple.
When the results of that poll come back, you need a way to display them. You need to decide which part goes where, which elements to hide and which to create, etc. The mechanics of this is what React (or a similar library) does for you, so that you could concentrate on writing logic instead.
> We're talking about React being overkill in a lot of places.
React might be overkill in a lot of places, but it's extremely hard to tell where and when. If you start with a simple hand rolled JavaScript app, at some point you might realize that maintaining it is a chore beyond one person's capacity, and hiring someone to do that for you is plainly impossible. You should have started with (React|Angular|Vue|whatever) in the first place, and now it's a choice between ground up rewrite in (React|Angular|Vue|whatever), or long stagnation and eventual death of your business. Do you want to take that chance?
Optimizing for developer productivity is the only safe bet in the majority of cases, unless we're talking about a personal project with no commercial value.
> Client side validation is not only about required fields, it's much more than that. Have you ever used a web store where you need to enter your shipping address, credit card info, etc? Address, zip code (or worse, UK style postal code), phone number, email, credit card number - all that needs to be validated.
A couple points:
1. The ~100ms between submitting a form and getting error messages isn't that big of a hurdle, in my experience.
2. HTML5 has validation attributes for forms, which can handle every one of your examples.
> If you don't want to submit a form with full page reload, you need to make an Ajax call with form info. When response comes back with a list of errors, you need to walk through the fields on the page and mark the relevant ones as invalid, with corresponding error messages displayed _close_ to the input fields. Of course that is doable in plain JavaScript, and doing that seems trivial for one form. Then you have to duplicate that code for another form, or abstract it in a library... Congrats, you're on your way to reinventing React. Or worse yet, Angular.
Negative. You can do an ajax call, and re-render part of the page (e.g., the entire <body> pjax-style). This is fast, doesn't cause the "flash" of a page reload, and doesn't require more than a few lines of js to setup.
> When the results of that poll come back, you need a way to display them. You need to decide which part goes where, which elements to hide and which to create, etc. The mechanics of this is what React (or a similar library) does for you, so that you could concentrate on writing logic instead.
Again, turbolinks on pjax has solved this problem very well. Just re-draw the bulk of the page. All the simplicity of reloading the page with none of the user pains of actually reloading the page.
> Optimizing for developer productivity is the only safe bet in the majority of cases, unless we're talking about a personal project with no commercial value.
I agree. But I don't agree React _benefits_ developer productivity. On the contrary, I think it (and its competitor frameworks) are responsible for a lot of wasted developer time and frustration.
> The ~100ms between submitting a form and getting error messages isn't that big of a hurdle, in my experience
Sure, it's the >100ms between the user entering the first value that might have an error and the user submitting the form that's a much bigger problem between validate-on-server-on-submit and validate-on-client-on-entry.
> But I don't agree React _benefits_ developer productivity. On the contrary, I think it (and its competitor frameworks) are responsible for a lot of wasted developer time and frustration.
After a not-very-long familiarization period, developers seem to be more productive with React (or probably Angular.) I know I am, and I've been using SPA frameworks (mostly React) for a short time after having done web lots of other ways for a very long time.
I've been professionally working on SPAs for the past 7 years. I'm familiar. I strongly disagree with the premise that it's in any way more productive. I'm, at a minimum, 2x more productive building something the traditional "Rails way" with server-rendered HTML, and a handful of JS enhancements where necessary vs doing a full-blown SPA. I'd love to do a showdown, and challenge anyone to a head-to-head non-trivial app build-off to put these competing notions to the test.
Everybody is different. You might be more productive at doing stuff the "Rails way", somebody else might not be. I know I won't be, simply because I don't know Ruby and/or Rails enough to be productive in it.
Not every organization can use this approach either. E.g. I work at a True Java Shop(tm), where voicing the idea of using Ruby for a back end service would get me laughed out of the room with a permanent label of That Funny Guy Who Likes Toy Languages.
Using React for a front end client application that consumes back end API built in Java is 10x more productive than doing the traditional "Java Way" application with server-rendered HTML.
> The ~100ms between submitting a form and getting error messages isn't that big of a hurdle, in my experience.
The issue here is not the length of time it takes for the response to come back, rather the fact that there is any delay at all. Client side validation code can be synchronous, submitting any validation info to the server makes it asynchronous by definition. State synchronization between client and server is a big enough problem as is, there is no need to add to it.
Consider this scenario: you have typed an email into a field, tabbed to the next field and realized that you made a typo. Our client sent a validation request to the server, and meanwhile you shift-tabbed back and corrected the typo. The server response came back with invalid indication for the already outdated state, what do you do?
This kind of issue is happening in real life a lot more often than you might think.
> HTML5 has validation attributes for forms, which can handle every one of your examples.
There is no input type for credit card number but there is a very basic and easy to implement algorithm that checks the credit card number validity. This alone is worth implementing client side validation, if your application has to handle payments in any form.
> Negative. You can do an ajax call, and re-render part of the page (e.g., the entire <body> pjax-style). This is fast, doesn't cause the "flash" of a page reload, and doesn't require more than a few lines of js to setup.
Double negative. Re-rendering part of the page received via Ajax call obliterates the current state of that page, or worse yet, a part of the page. User started typing and the page reloaded, all their input - and even which field was focused - is lost. This is a very undesirable user experience.
> Again, turbolinks on pjax has solved this problem very well. Just re-draw the bulk of the page. All the simplicity of reloading the page with none of the user pains of actually reloading the page.
It's actually vice versa: all of the pain of actually reloading the page with no real benefit whatsoever. If you are going to reload the bulk of the page, might as well reload the whole page, just to have consistent state. But that brings us to square one: why having all this complexity in the first place? Because we want user interaction to be smooth, quick, and painless. This implies eliminating roundtrip delay, however small it is.
100 milliseconds might not be much but it can easily turn into 10 seconds if the server is overloaded. 10 seconds delay after submitting a _validated_ form to complete sales transation and display a success message is not a big deal, the user has already made a decision and now they're going to see the positive result. Your site is slow == that's ok, I made the purchase anyway.
10 seconds delay to validate form values and display an error message would most probably result in a lost sale. Your site is slow == it's bad, I won't spend my money here.
It's as simple as that.
> I agree. But I don't agree React _benefits_ developer productivity. On the contrary, I think it (and its competitor frameworks) are responsible for a lot of wasted developer time and frustration.
That's a highly subjective assessment. Do you have a way to measure productivity gains or losses? I don't either, just some anecdata.
About a year ago I needed to build an app for my ongoing personal (at some point to be commercial) project. Think a simple portal for adding, editing, and deleting entities. I decided that since it was internal use only, I can ditch the fancieties and do a quick and dirty old style app: server side logic, form submissions, full page reloads, etc. Back to 1999.
I spent a month of evenings trying to get it done, and didn't get halfway before ditching the effort and rewriting it as simple front end single page app + simple back end that exposed stateless API for the client to consume. That took me 2 weeks worth of evenings.
Lesson learned: never again, there's just too much mess with server side HTML templating, state propagation between page loads, form validation, etc etc. I don't even want to think about partial updates that _still_ involve server side HTML templating but combine that with a double dose of state propagation and synchronization. I didn't even get to writing any tests for that server side, simply because abstracting templating code from actual logic code was painful.
It can be argued that I did not use the best server side framework, did not know what I was doing, etc. That's actually my point: if doing old style web apps was so much easier, I should have been able to complete it fast without much sweat, no?
> It can be argued that I did not use the best server side framework, did not know what I was doing, etc. That's actually my point: if doing old style web apps was so much easier, I should have been able to complete it fast without much sweat, no?
Having seen many similar discussions I came to the conclusion this might really be something of a generation gap. For those of us who spent their youth developing web apps back in the day, doing it the "old" way (that fortunately still works) seems natural and easy. And in some cases it's ridilously easy. If your needs are fairly typical (like the CRUD you describe), you might not even need to do that much - Django Admin or Laravel Voyager will take care of these.
No generation gap here. I was building my first web apps in early 2000s as well, with Apache and mod_perl. That was exactly the point of that experiment: hey I recall this stuff was _easy_, I don't need it fancy, let's take the modernest server side framework and go for it.
No thanks, not gonna do that ever again. Rosy glasses are rosy, and cleanly separated client side app + back end API is clean and separated.
> For those of us who spent their youth developing web apps back in the day, doing it the "old" way (that fortunately still works) seems natural and easy.
Spent my youth doing it the old way. Much rather use either React for nontrivial apps, still, now.
> They're forms that submit stuff to a server and get a response.
So it sounds like they're not actually doing any of the things you mention. For basic forms that just submit, of course React is overkill. Hell, a software developer is overkill for that. For any of the things you mention, React or another similar UI framework makes the project much more manageable.
Thank you for actually answering the question! Your list of functionality is the first that I've seen which actually shows where React really is helpful.
The issue seems with moving responsibilities that the server handles really well to the client (namely, state and session management). I think it's less a matter of Javascript-framework vs no framework, and more a matter of SPA vs. non-SPA
This hits the nail on the head - it's about SPA vs MPA. Component-based architectures are a proven and a win on FE for more complex interactions, but that doesn't necessitate an SPA. We had a large SPA with a REST API backend and were finding it really difficult to add more features to the FE due to the inherent complexity of the SPA.
We decided to split it into an MPA, adding React components to server-side rendered pages as needed (along with intercooler and stimulus when we don't need the power of React).
Things have become much simpler and quicker to develop, allowing us to focus on the user features instead. It's easy to forget how much you get for free from the browser and traditional HTTP/HTML client-server mechanics, along with the ease of having full access to your database and data model in your templates - and makes you wonder if going with an SPA first is throwing the baby out with the bathwater.
Mmm, I work on a multimedia sequencer (timeline, viewport, etc) in the browser, it would be utter hell if the whole thing was put together via manual DOM updates in something like a Backbone View render function. I know this because when I joined the company, they were trying to do it in Backbone. For a project of that scope, this was a great way to introduce a large surface for error and maintenance (the project kicked off right when React was launching and Typescript wasn't public). Leveraging React as an interface to updating the DOM removes all of those concerns from your implementation work. That's a big productivity boost when you're trying to build a multimedia sequencer. More so when your team is junior-dominant.
> when React was launching and Typescript wasn't public
Minor correction, React came out about 7 months after Typescript. That said, Typescript had a promising birth but a rough infancy, so I wouldn't blame anyone for considering it released only after 1.x versions starting in late 2014.
I've worked on a few applications with 'rich/complex interactions' and everyone on the team was opposed to using any sort of JS framework. That was nice because it was relatively easy for anyone to trace through the application as needed.
In my experience if the application state is overly complex then that is a result of the design, and that usually leads to a bad experience for the user.
> I've worked on a few applications with 'rich/complex interactions' and everyone on the team was opposed to using any sort of JS framework
Ah but if you actually looked at what you built, I am 100% sure you folks built your own framework instead. A framework that has no community support, no stackoverflow articles to help out and most likely minimal explicit documentation. You will never be able to hire talent that already knows the tools for your framework. You'll be spending all of the next eternity cobbling together features that you could have had out of the box had you adopted somebody else's framework instead.
The choice is never "use a framework" vs. "don't use a framework". The choice is "do we use somebody else's framework" vs. "do we build our own framework". What you implicitly and unknowingly chose was the "we are gonna build our own framework" option.
In my experience, the minute the few folks who pushed the "no framework" option leave the company, all the rest of the developers smile and scramble to replace the hot mess of a codebase with something using an industry accepted framework.
i've seen a shop start as a "no-framework" effort and it did indeed become an "internal framework" project. imo it was a strange choice, given how much effort had to go into maintaining and bugfixing the internal framework...mostly issues that would have been flushed out in something with wider support.
before being involved in mostly "modern saas", i was heavy in electrical/electronics mfg and there was always a tension between "not-invented-here" and "borrow-buy-build" camps. NIH would stress that all components in our designs should be in-house (electrical metering, data acquisition, etc) while the BBB camp would turn every effort into a 3rd party integrations exercise. at times this even applied to the factory floor. i seriously know a person who was like "hrm, lets build our own pick-and-place for the smt line..."
> i seriously know a person who was like "hrm, lets build our own pick-and-place for the smt line..."
I mean, how hard could building our own pick & place be, right? Once you strip out all the bloat the vendors add to make a sale, it is nothing you couldn't do with a couple servos and a $5 Arduino, right?
I think the "build everything ourselves" attitude comes from a not at all understanding opportunity costs and comparative advantage. Plus in the software industry, tons of developers I've encountered are super paranoid about "vendor lockin" without realizing that once they roll their own library/framework/pick-and-place-machine they have effectively locked their employer into a vendor as well. Only instead of a third party vendor with all the benefits that may come with it, they've "purchased" a product from a really shitty vendor--themselves!
It is never a choice between:
"Platform or no platform"
"Framework or no framework"
"Vendor lock-in or no vendor lock-in"
You will always be on a platform. You will always be using a framework and you'll always be locked into a vendor. The trick is to make sure you don't lock yourself into a shitty vendor who makes a shitty platform or framework.
Not directly related to web front end but having extensive developer experience I tend to shy big frameworks (with a couple of exceptions) . Instead I am using set of battle proven libraries that change depending on needs.
My reasons are:
I do not need to learn a monster
If framework goes down/becomes unpopular etc. etc. I could care less
Libraries are easily abstracted and replaceable.
Yes I/team do end up implementing our own framework but this framework is very tiny comparatively to you common monstrous frameworks and any sane person can get a grip in a day or less and it only has enough to accomplish a project.
Also it can be easily sliced/diced and needed accumulated bits and pieces can be used for next project.
> A framework that has no community support, no stackoverflow articles to help out and most likely minimal explicit documentation.
But there are gains too. The custom framework is probably much smaller and less complex, easier to know 100% of. It can be stepped through in the debugger. It's much easier to change and add features that your company needs, there's no outside bureaucracy to get in the way.
I disagree you're inventing your own framework too, there is a distinction to be made between libraries and frameworks. Just because I'm not using a framework doesn't mean I'm reinventing it, I could be using something like knockoutjs instead, or even postbacks.
Mark my words, you’ll be spending the rest of your days poorly reinventing what was already done in a real framework. I’ve seen what comes of these “I don’t want to use a bloated framework” shops and it is never pretty. The second the people who eschew mainstream frameworks leave, the devs who are still are around will quickly rebuild around a real framework.
In my experience, when it comes to rich interaction, the question isn't whether or not to use a framework -- the question is whether to write your own framework from scratch and discover all mistakes you can make on your own, or use one that already exists and builds on best practises as well as we know them.
In my experience if the application state is overly complex then that is a result of the design, and that usually leads to a bad experience for the user.
Though it is possible to overengineer this, you're probably underestimating ho seemingly simple apps can justifiably have complex state.
Say you make API calls. Your application state just grew to reflect
1. Pending request
2. Request succesful or failed
3. Request result or error
4. Finished request.
Redux apps track all of this explicitly. The alternative to this is not reducing statefulness. It's just ignoring it with all the brings.
Oh I agree with that, I just think that the internal state of the app can be more easily managed if those potential application states are explicitly identified during the design stage.
They're forms that submit stuff to a server and get a response
What kind of response? Asuming no React,
a. json
b. SSR-html?
If,
a. Now you've got to process that response, handle errors and finally render into html. You'll be either imperatively replacing DOM nodes, interpolating string templates or both. Probably re-binding event handlers after that.
b. You'll be merging your server-rendered html to your current view. Re-binding event handlers if you've got any interactive stuff (e.g your SSR came with a modal link) and god knows what else.
I just use vanilla JS. For transport I use JSON over Websockets (with fallback to long-polling if the user is behind a proxy that doesn't support Websockets). I format the messages like id+command+json-payload. If the message contains an id, it calls the callback function given by the API request. If no id is given, the event listeners for "command" are called. In a higher level there are more events that can be listened for.
For layout I use a mix of both pure and imperative functions that either creates a new element for every state, or modifies existing elements - however all isolated in a component or widget.
I try to use standard HTML5 elements rather then creating my own. But they are often inside a component or widgets which takes care of events like keypress, mousedown etc.
There are no string templates! No server rendering, just API endpoints. There is no JQery, there is no framework - besides the functionality for handling server messages and passing data to event listeners and callbacks.
Some components and widgets are generic and can be reused in other projects. But most are specific to the app itself.
The advantages is that it's simple, fast, and customizable. The disadvantage is that layout is a bit tedious using appendChild instead of XML/JSX.
I use CSS for styling. I like CSS very much probably because I used to do web apps before CSS existed. To change theme you just change the .css file. Components, widgets and elements are not aware of theme and style, that's all handled by CSS.
Animations are handled by CSS, and different screen sizes are also handled by CSS. Sometimes you need to the change components/widgets though.
The only advantage I see with frameworks is that you get to write XML/JSX/HTML, which makes it more easy to move things around vs just using JS functions and appendChild. But when looking at React apps the components are broken down into individual files anyway, so it's difficult to get an overview.
Probably the biggest advantages of going vanilla JS eg. no JSX nor frameworks, is that debugging becomes easier, and you do not need a build pipeline, just refresh or hot-reload individual functions. No bundles or package managers needed.
That sounds like a great framework. Let us know when you release it. :)
The points made elsewhere in this thread still stand: a bespoke framework might be conceptually simpler and much easier for a solo developer, but for larger apps that require a team of developers using a popular framework would be much more productive overall, as everyone is or should be on the same page with how features are implemented. This often results in poor UX where specialized knowledge about the framework is needed to improve performance and scale, which you as the author of yours know by heart and find it much easier to achieve similar or better results.
I'd say neither approach is inherently bad. Use whatever delivers the best UX for the project at hand, but know that you'll be paying a price when that approach reaches its limits.
For short living projects, say two moths or half a year, I would probably go with an existing framework, for that initial speed and reuse of existing libraries/plugins. But for anything you would spend one year or more on, I would build a custom solution which optimizes for whatever is the domain priority, for example front-end performance, speed of development, etc. If you use your own "framework" it will become stable over time, where as a popular frameworks like React or Angular will just keep changing and eventually reach end of life support.
You forget the most basic response. A full HTML file with a corresponding reload. None of what you said applies. I'm a big react fan and advocate too but sometimes I think we forget about the basics.
hey, we all love how fast HN loads, not gonna argue!
On the other hand, I don't think it's usable for other crowds. I spend most of my day inside vim, so I'm used to switching windows and going back all the time but I don't think that having to load a submit form in a new page, losing all context of the conversation tree is particularly usable.
Sites or "apps" relying heavily on full page loads are, in my experience, much faster than their "efficient" XMLHttpeRequest counterparts, in practice. As in, click, thing happens, thing is done happening, much faster. We even have examples of this with the same "app"—Gmail, for example: Basic HTML, "mobile", standard, and, though I think it's gone now, Inbox. At least four versions. Basic HTML is less janky than "mobile" on mobile, and much, much faster than standard or Inbox, despite "inefficient" full page loads.
> No one is going to sit through a full-page reload on every form validation.
Why not? Most of the pure HTML forms I use load at least an order of magnitude faster than the complex SPAs I use because there's so many fewer network requests. You can make SPAs which are as fast but I'd say fewer than a third companies, even very large ones like Google or Facebook, succeed at doing so and an even smaller percentage have thought hard enough about error handling. On a daily basic I use sites from Google, Twitter, Facebook, etc. where I have to do a reload anyway because their developers were unaware that network operations require timeouts, retries, and that you need to provide UI feedback for all of those to match what the browser provides by default.
EDIT: to be clear, I like that we can do a ton of very complicated things in a browser now — we've been working towards that as an industry for the last 3 decades and it's great that it's largely arrived — but I think it really hits the failure of companies to skimp on resources for anything which isn't critical to the default user experience. Duplicating built-in functionality should be seen as an expensive commitment, not the default unless you're willing to devote the extra resources needed.
Yes, ideally, a SPA could be better (i.e why re-send the parts of the app that didn't need to change), but most of these apps are actually way slower -- either because the developers don't know/care beteer, or perhaps more probably, are not given the time to implement the perfect(-ish) solution.
A full html page reload isn't much slower than react/redux doing a json fetch, but it may appear so since most of the content doesn't flash, new content comes in with an effect etc. And I agree, this looks more slick.
pjax is about a decade old at this point, dirt simple, and easily handles re-binding event handlers. Check it out: https://github.com/defunkt/jquery-pjax
Turbolinks is a more contemporary library that does much the same.
Ok, so don't do this, but
script src=/fetchmessages.js?t=1
Which returns document.write(messagecontent) //if message
document.write(script tag t=n+1)
If there are messages, server returns them all immediately, plus the repoll JS, and if there aren't any, it waits N seconds before returning just the repoll.
This could fit pretty well into an IRC single channel model, where you just stuff all the messages into a single place. For something more akin to MDI/TDI where you have different message displays depending on who you click on, it's trickier to just shove the messages in at the end --- although, if you were a terrible person, you could do something like use the chat id with a prefix as a class name, and use that to show/hide. You still need dom manipulation to add new chats.
I got sidetracked searching for pictures of the multiplayer part of the game Vagabond's Quest.[0]
I wasn't successful, unfortunately, but a number of the pages outside the login wall were archived.[1]
I think you might find several of these pages entertaining,[2] especially if you're familiar with text-based games (especially MUDs and MOOs[3]) and a Dungeons and Dragons-esque setting. The items, combat (PvP and 1-player), and any other aspect of chance were transparently dice rolls and modifiers in-game.
Because this is Hacker News, don't miss out on the >20-year-old anti-hacking mechanism.[4][5] Keep in mind this is for a sequel or companion game that came (IIRC years) after the original.
With that stage set, picture a black background and some grey text with scattered red/green/blue highlights. The logo was probably the only image. The page would reload every few seconds, but you could manually reload and see new messages sooner. If your username was in a chat text it would make the text larger (both the line and your name, I think).[6] Maybe under a few other conditions too: if you were fighting someone, if it was the admin/mod/dev(!) Falados, I think you could add a few of your own word filters at some point. Originally it was just your username in the line, I'm pretty sure. The combat moves and rolls were also part of this chat stream. I remember being in busy rooms with dozens of people, frantically refreshing, trying to read the top of the screen quickly before it was lost.
It was great!
I hadn't quite connected the games to programming yet, but I imagine the Hall or Arena you "joined" for Player vs Player combat and chat was by visiting the page of HTML generated by Perl scripts (I remember URIs ending in .pl, maybe some CGI thing?) that had a meta tag to trigger the refresh. It had a regular form with a submit button and the page reload was convenient because that was the only way to see your message or anyone else's new in the page.
I'm amazed how clearly I remember playing, if the details are a bit fuzzy. Looking for pictures I came across this,[7] and am excited to read through and maybe check out a beta. Thanks for prompting the search!
I think React's use-case is multi-model visualization tools (e.g. a Bloomberg terminal) and cross-visualization editing tools (e.g. Business Intelligence software.) Things where one data source gets data-bound not just to to multiple views, but to multiple view controllers which expose the data differently.
with react native I am able to leverage native device apis in Javascript without writing the underlying Swift or Java bridge, there are so many super popular libraries (5k + stars on github), image pickers, contacts, file systems, ui elements that support both platforms and have huge open source support. I am able to create components faster in React or a framework with up to date documentation/testing than using a custom component solution and speed is always in fashion.
I've been writing vanilla javascript on the frontend for a recent project. It's really not bad. Much less cognitive load compared to writing react. Easier to read too
For individuals making small sites, it's absolutely overkill.
For my job, with hundreds of developers working on a giant SPA blob with constant feature creep, frameworks like React are a necessary evil to maintain the insanity.
I think it's about the number of developers involved.
If you have a small team of 3-5 people who understands the architecture as a whole and can keep up with most PRs then I don't think a UI framework is necessary.
However, if you have many teams working on the same project and/or cannot keep up with every single PR merged then I think using a UI framework is a must. It forces you to stick to strict rules. Although you will start writing boilerplate code, it makes the entire application much more modular, and easier to reason about.
To me there’s nothing wrong with React, but everything wrong with people using it in inappropriate contexts.
For instance, I’ve seen a growing trend of people creating a whole DOM using React JSX so that they can use React’s server side rendering pipeline. <body> tag all the way down. But the actual reactive components make up about 30% of the page. So you’re wasting download and JS processing time (a serious concern on low end devices) hydrating a lot of elements that won’t even ever change. It’s stupid and it needs to stop.
But for fully interactive webapps, React is great.
...aren't the static portions uh...rendered server side? And possibly even the dynamic ones cached when common? If not, I'm confused about what SSR means in this scenario.
I don’t understand why you would serve up the React library if you’ve already rendered on the server. I use React server side and then serve the static HTML.
>So you’re wasting download and JS processing time (a serious concern on low end devices) hydrating a lot of elements that won’t even ever change. It’s stupid and it needs to stop.
You hydrate the markup because you want to render subsequent state changes in the client. Meaning much smaller downloads (just the relevant data) between route changes and so on.
That’s often a bad bet though. Obviously it depends on what kind of site you’re operating but a lot of places have a very steep drop off after a single page view. If that’s your case then you’re optimising for an edge case at the expense of the majority.
I see you concern but right off the bat here is a very good example of where this kind of frontend pattern works very well, even for site that is essentially just static content from a user perspective:
I suggest you open the network inspector and see what happens when you hover over links, navigate and so on. The experience is very fast and responsive on initial page loads and even faster on subsequent navigation:
The initial page load is as fast despite of hydration because many of the requests are non-blocking and/or happen after the raw page content is loaded, including JS scripts.
Further navigation triggered loads just load small amounts of JSON while many of the assets and common UI elements are already there. In this case the data is even loaded while you hover over a navigation link.
Yes, this assumes that you actually navigate through the site and yes it does more than you sometimes need if you just view a single page.
But the trade-off seems to be very good here from my perspective, especially because one cannot know if the visitor will navigate on the site past the initial page load or not. The content on this site is also structured into small subject-pages (see: the docs sections), which synergizes well with this frontend pattern.
On top of that comes ease of development, tooling and automation around the react ecosystem (you'll find similar in other frameworks). And the mere fact that there is much less coupling between the rendering/gui side and the data/server side, even though most of the site is rendered on a server, because the frontend code lives in one cohesive framework.
Perhaps unsuprisingly, React's own web site is a great example of how to do React right. By the looks of it it's not loading the entire page into Virtual DOM, it's got code splitting, loading on demand... all the absolutely right things.
A lot of React sites I see in the wild have none of this. They have a multi-MB JS blob that gets downloaded immediately on load and parses a giant JSON blob stored in the HTML that represents the page state.
I'm not saying React isn't useful or that it can't be done well. I'm saying that in a lot of cases it just isn't done well. Whether that's down to a lack of education or a lack of care I don't know.
One of the reasons is the difficulty of writing reusable interactive components with server-side rendering such as maps, rich text editors, autocompletes, hover previews, menus.
The components work with data. Let's say you need an auto-complete component - 500ms after the user starts typing it will query the server and get some data back. You can describe the returned data in partially rendered HTML too but the attribute-based descriptions are often quite limited and for ease of use from the JS side you're likely to end up with embedded JSON as a general data description language with unlimited nesting, which naturally leads you to a client-side templating language.
The components are also likely to re-use each other, giving rise to the need for a module system to track those dependencies and make sure they are all loaded when you go to a specific page based on the components being rendered. If you use partial rendering you would also need to ensure to track any components used from the partial html endpoints have their JS included in the page, capturing any of their occurences, making sure to attach the right event handlers to enable interactivity.
Sure, one could ostensibly write a set of components that use JS under the hood and add them to the back-end. They could also write a server-side module system that ensures that the right scripts are included in any page that uses them. They could do all that.
But then that would work only for a single backend ecosystem, such as e.g. Rails. There is no way this component library would be able to compete with a unified client-side ecosystem in terms of features, flexibility or battle-testedness. Additionally, if you want to try a different language on the backend, you won't be able to - unless you're willing to pay the overhead of a secondary service meant to only generate the HTML and manage the JS plugin system.
And all this is before we even consider alternative clients (e.g. mobile)
For reference, here is a "simple" app - a parametric EQ designer that supports doing pink noise measurements of speakers (or headphones if you have the right equipment) with FFT in the browser: https://eq.spion.dev/ - works as an entirely static website :)
Typically what people say is "oh most websites won't need THAT" but the appetite for interactivity only grows with most companies, so you could easily end up in a slowly boiled frog scenario where your serverside HTML-JS mishmash is an absolute mess.
Static websites don’t maintain state on the server. They can be as dynamic as you wish on the front end. But if you do a page refresh you get the same page you started with.
1. Componentization. I've found this to be super useful and helps me build items that I can use in multiple places. Pair that with something like Storybook JS - and you have an app that can be broken up into puzzle pieces with high reusability. This helps isolate the business logic from the UI. Granted - a similar effect could be pulled of with HTML templating.
2. State Management - Love love that I don't have to handle UI updates when the data changes. This I believe is the whole "reactive" model [if I'm not mistaken]. Consider building a Rich Text Editor, and the user is typing, he uses keyboard shortcuts to enable bold, underline and italics. How do you update the UI to reflect those have been selected. Listen to the shortcuts, and add the necessary class? Great, what if that shortcut had to kickoff ten more things? Another method also adds a contradicting class to that same element. At some point, the logic becomes super entangled, and it becomes hard to track where and why. With React/Redux - life runs like an FSM. Each update/action can be traced.
[though I use MobX because it takes less effort]
P.S.
I also don't recommend React for Single Page things. It's unnecessary bloat. Complex in my head means multiple distinct components (like > 5).
- Using a UI framework maps well to working with UX designers in that you design the interactions of a given component in accordance to UX spec.
- Combining multiple components in a given page to transform or process user interaction becomes simpler as the components are plug and play with the state of the page.
- Combining multiple pages where state can move across an entire application can allow for a seamless user interface
- Employing techniques like code splitting and Server Side Rendering allows for smaller js payloads with an incremental approach to assets versus loading everything at once.
The largest advantage UI frameworks provide is that it makes what you would normally do with javascript a lot more _composable_. You can do the same with vanilla JS, but it gets a lot more difficult when you attach events to the DOM, it's hard to reason about how you might organize your code which doesn't scale well across teams.
I think the simplest way to consider the advantages of a UI framework is to build a search application from scratch with vanilla JS. You can get pretty far but once you start adding in things like pagination, it can get a little hairy. jQuery and it's libraries can help you there, but there's a good chance that you're adding in some overhead to the load times of your page.
If you rebuild the application with a UI framework and employ practices like code splitting, the code (usually) ends up being a lot more reasonable and easy to extend (which businesses tend to like). Adding features becomes less about reasoning about which DOM event fires what and instead more about transformations in state with the components subscribing to state instead of DOM events.
React is cool if you want components. Web components had crap support because browsers needed to implement them; React you could use immediately.
I agree Redux isn't an improvement there, media queries for user preference[0] were. However, rendering details aside (this may still use a CSS class), you might choose to give a separate option inside your regular UI or have some other interactive blob that is colorscheme-aware but needs to be told that manually. Maybe you have lots of options. The app-colors state can be set to some explicit value initially or default-inherit from the environment, but it still has to be tracked somewhere that isn't the stylesheet. It doesn't have to be Redux but it does have to be something.
Relatively soon I expect web components will start to eat into the UI framework nonsense.[1] React never really hid the need to know HTML, CSS, and JS well just like jQuery didn't. They made it _easier_. They also encouraged a plug'n'play ecosystem that made it easy to kinda-sorta hack together what you wanted -- without having to do much reading. Bold prediction: In the future we'll look back at a lot of "React developers" like we do at "jQuery developers" now.
[1]: https://caniuse.com/#search=web%20components shows Safari as the only partial out of major browsers, plus some in the long tail. I've only poked at it a bit recently, they were a mess last time I tried (years ago)... it's much better.
For the specific example the article gives, you probably should implement it by having a top-level component change one of its class identifiers and having the CSS properly select light mode / dark mode all the way down the chain.
... but the purpose of state is to keep track of whether you're in light mode or dark mode. You don't want the arbiter of that knowledge to live on the DOM if you're doing a web app, because you need to reason about who is allowed to change between light and dark mode and the DOM is global state; anything in your app could be editing it. State (among its other uses) serves as a gatekeeper on the ground truth of what mode you're in (and if all changes to presence / absence of the class in question pass through that component state, you're good to go).
While I'm not an old dog myself, rather a puppy. I share your sentiment. SPAs or over-jsed pages are problematic for many reasons.
At the moment this is my list when coding frontend on my own projects:
1. The less js the better.
2. If you can solve a problem in a few lines of native js, go for it!
3. Some complex "component"? Use React, it will save you a headache.
Why like this?
1. A lot can be done without js. Solutions are many times simpler and easier to modify.
2. A lot of components, like models and so on can be done in a few lines of javascript.
3. I think React isn't bad per se, many times it's a cleaner solution over native js when it comes to complex components, which can be required by designers, shrug.
Hey - author here. React is just simpler for me to reason about. A app has many pages, a page has many components. I can write components; I can write Redux/MobX code; I can pass data between components. Things work.
But absolutely, my speed of development when using React is lower than when I'm using server-generated HTML in a Rails app.
On the other hand, I can do things with React I can't do with server-generated HTML.
On a serious note, I do agree with you. oftentimes things are overcomplicated just for the sake of 'javascript'. Once you get used to setting up a project a certain way you may not stop to consider whether or not it's the best for the specific project.
I've always preferred hand-crafted websites with readable view-source over frameworks, but I finally switched to react so I could keep my site's background animations persistent over pages. It' somewhat unfortunate that its still not possible without some JS (in this case, a framework that's swapping out portions of the page).
> I'm one of those "old dogs" who don't quite get the advantage of using React in the majority of the cases it's used. This article didn't really help...
When you feel like this, just remember two words, "developer productivity". That will be the excuse (reason?) for using it.
Point 1 in this article is totally misguided. "State management is how you mitigate prop drilling" implies that when prop drilling is not a problem (i.e. most of the time), that you don't need to think about state management. Even if you're prop drilling, you need to make sure data is not stored/updated in multiple places in your app in order to keep the UI consistent.
The one redeeming part of that section of this article is that it links to another article that isn't as wrong. The linked article details when prop drilling is and isn't a problem. And most of the problems it describes can be reduced by using TypeScript. https://kentcdodds.com/blog/prop-drilling
Hey - author here. I did not mean to imply that state management is only useful to mitigate the prop drilling problem. In fact, in #2, I give another example where state management is useful.
The first section reads as if it's explaining why state management is needed at all, not as if it's giving one example of something state management is good for.
I couldn't make sense of the second section either. It sounds like it's recommending a global store as the solution to updating a single component. The correct state management for a single component is what comes with React.
So let me just check that I understand this correctly.
A whole bunch of problems that were "solved" (or at least, had fairly good solutions) in the context of desktop GUI toolkits have resurfaced in the context of application development confined to the web browser.
Almost none of the previous "solutions" are usable, because of the specifics of the theoretically portable "browser platform" for which all this development is taking place.
So instead, an entirely new set of frameworks are slowly being developed to solve the same set of problems that desktop developers worked on 20+ years ago, and are slowly converging on similar approaches?
I don't believe the concept of one way data flow was in the old desktop app approach was it?
Nor was the idea of "props" i.e. a consistent mechanism for pushing parameters down to subcomponents through a consistent and flexible interface.
Nor was the idea of the render function - giving components a consistent function used to display itself.
Also gigantic leaps are being made in the area of simple componentisation - we are now down to very pure functions that handle a very constrained task.
State management in ReactJS especially with hooks is giving much more simple and clean mechanisms for handling cross cutting.
Old desktop applications, because they did not have a core application "cycle/flow" and were not arranged as a tree of components, had a tendency to end up as a spaghetti mess of complex highly coupled interactions with the whole thing risking collapsing under its own weight of complex interactions. Modern frameworks like ReactJS address these things by reducing complexity, increasing modularity via components, giving applications a general structure which is a hierarchy of components, giving all applications a consistent model for data flow, forcing a simpler mechanism for data sharing via the one way data flow rather than the more highly coupled two way data binding, and lately, emphasising the power and simplicity of plain functions over classes, objects and inheritance.
Surely there's other innovations too I haven't thought of.
Characterising modern web development frameworks as somehow only now catching up to 20 year old desktop development does not seem correct to me. Such an idea does perpetuate the myth though that the JavaScript world is full of dumbos badly trying to reproduce what all the smart "real developers" did a long time ago.
To be honest, most web apps remind me much more of the old "software development tools" that used to come (still come?) with databases from 20-30 years ago.
Web apps seem much closer to the database read/edit interfaces from that time (though much, much prettier and generally more sophisticated): there's a simple model (which does include one way data flow) - there's a data store somewhere, and the task at hand is some combination of:
1. present that to the user.
2. allow the user to enter new data.
3. allow the user to modify the data.
Oracle and others before and after them had a rich set of tools to develop primarily terminal-based applications for these purposes, which began to get "graphical" sometime around the end of the 1980s. These days, I suspect almost nobody uses that toolchain, and instead does the exact same tasks (again, much prettier, more visuals, more interactivity) via the browser.
Desktop applications started moving away from this model as "creation" apps started to emerge, probably starting with "desktop publishing" and moving on to include graphics in general, audio and video. By the time you get to Photoshop, for example, considering the task to be moving data either from or between a data store and a user interface doesn't really get you very far (though it's still an important aspect).
It is not true that old desktop applications did not have a tree of components. That's a core aspect of every desktop GUI toolkit that i'm familiar with. One thing that the desktop GUI toolkits didn't do very well was that tended not to support the original conception of MVC (not the current one found on the web). For example, when you interacted with some widget, it would change its visual state before anything took place in the "model". My vague impression of quite a few of the web component models is that they at least avoid this some of the time - the user interacts with them, and they (mostly) only change their appearance to display a change in the model.
All technology goes through two somewhat independent cycles, continuously:
1) The bloat cycle
2) The disruption cycle
The bloat cycle is where you have a “platform” that does more and more until it has a lot of features that most users don’t need. Eventually the cost of supporting those random features gets too high and there is pressure on people to hack together an alternative micro-platform that does some arbitrary subset of things. If that subset is actually a superset of “must-have features” for a large number of users the micro-platform becomes a new platform and the cycle repeats.
The disruption cycle is when there is a singular feature that is prohibitive to add to the framework, because of past decisions. Businesses working on the platform will just work around the hole, or fill the hole in a hacky way. This creates pressure for someone to create a new platform that solves that one unsolved problem. It can leave many solved problems unsolved and still ascend to dominance under conditions famously described in “The Innovator’s Dilemma”.
This would be something like “multiple users work on the same surface” or “works the same-ish on literally all the OS’es” features of the web.
These cycles might seem pointless, but they’re not because you end up in a different place than where you started. You get some fundamental simplifications and new features each time you go around.
Ideally as a user you want to time your entry into the cycle so you don’t have to deal with the regression phase.
It's so frustrating. React/JSX, and maybe Immer, is actually really good--it's revolutionized the way I write frontends. But I'm wary of bringing these tools into products, because I know that then I'll be having to constantly fight to not bring in whatever global state management nonsense people on my team discover first. If you stick around long enough you see global state systems collapse under their own weight into trashpiles of unintelligible code, but for the first quarter or so, it seems really simple to just pull the whole application into global state.
I think these cycles are just results of human social behavior around software platform technology; whether they have a point or are pointless -- they can be either. Sometimes it's two steps back one step forward, other times the reverse.
Genuinely curious, as someone who wasn't building desktop apps 20 years ago, what are the toolkits you're referring to?
When I google "cross-platform desktop frameworks", the entire first page of results are all about the modern toolkits like Electron, Photon, ReactNativeEverywhere, etc.
So it seems like your complaint is many years too late at this point. Maybe there were solutions to this 20 years ago, but if I'm starting from scratch today trying to build a cross-platform app, what I'm looking for are frameworks that are mature, well documented with lots of tutorials on how to do basic things, and have a large community around them for getting help on e.g. stackoverflow. It doesn't really matter what has been around longer, if the newcomers have surpassed it on these fronts.
Qt would be an example of a desktop UI framework that was around 20 years ago, and is still popular today. It's also far more mature and stable than Electron etc, and better documented.
I don't know whether there are more Electron apps these days around than Qt apps, but it's certainly neither outdated nor obscure.
Qt is already mentioned. GTK+ was also around 20 years ago (actually quite a lot more than that). For people dealing with media stuff, these days there's also JUCE though that is a relative newcomer. There was also FLTK, which I think it still around. Oh, and WxWidgets which was a very thin wrapper around the native GUI API on all 3 major platforms.
The problem is that in the days before web UIs is also a different landscape in terms of the computing resources available, and the amount of security required. These were the problems that applications were focusing on solving.
Because of this, the tools that developers were using were different, and don't make as much sense nowadays.
Well, first of all, the problem in its most basic sense doesn't even really exist for many places where you would use "Qt, etc". You wouldn't be reacting with a remote server or if you are you have a stateful connection with the remote server. That's true whether the "remote server" is just a database server or something more specific.
But going somewhat deeper (and based on reading all the defenses of "React, etc" in the comments thus far), the fundamental difference is that if you were using a desktop GUI toolkit, you would almost certainly assume that there's essentially zero cost (or almost neglible cost) to the communication between the data (model) and the GUI. This is (one of the things) that is so different for web development, where everybody is struggling to optimize away roundtrips back to the data (server), because it actually costs.
Put differently, whatever protocols might be implemented inside a desktop GUI to talk to a not-in-memory data source, the protocols are almost certainly designed for precisely that purpose. Whether its a vendor-supplied connection to an SQL database, an application-specific serialization/deserialization protocol, some IP-based message passing protocol ... whatever, it will all have been designed for the express purpose that it is being used for (more or less).
HTTP? Ahem. Granted, web app development has augmented HTTP in various ways (most dramatically would probably be websockets/webrtc). But fundamentally, the state management issue in a web app originates with the fundamentally stateless communication protocol being used between the app and the data sources/servers it is dependent on.
- The classic desktop GUI frameworks (Qt, wxWidgets, MFC, Swing, WinForms, etc) are based on a completely OOP inheritance and ownership model. You are responsible for instantiating widget instances, configuring them via setters, appending them to parents, and likely cleaning them up as well.
- It's been long enough since I've done desktop GUI dev that I can't speak to the state management aspect much, but I can certainly say that at the time, my own programming understanding was limited enough that I would have said my "state" was "whatever items are currently inserted in that ListBox". (I knew that forms of data binding existed, but didn't grok them yet.) I do remember Swing's JTable widgets have a distinct MVC setup, at least.
- Web dev is _heavily_ concerned with the number of bytes being delivered over the wire, so it's not just the number of round trips, there's the obsession with trying to find the tiniest library that can do what you need (combined with the nonexistent JS standard lib). With the desktop? Who cares, let's link in 50MB of Qt DLLs! (yes, yes, insert Electron joke here.)
>if you were using a desktop GUI toolkit, you would almost certainly assume that there's essentially zero cost (or almost neglible cost) to the communication between the data (model) and the GUI
Which is true, because model is what you’re working with and binding to (along with a controller). You can populate your models from a roundtrip data source, if it is not local. How is that different from web apps?
I think this is the good old “ancients were less smart” stereotype. Client-server and 3-tier apps didn’t begin with web. Heck, even lan latency/throughput/cpuclock were comparable to what modern internet timings impose.
Some of what you've said here strikes me as true, as long as you limit the domain to "applications" aka "database client-server front end foobars".
It's really not true for any desktops in the "creation" realms that I mentioned. The data model for an image manipulation program or a spreadsheet or a DAW or a document preparation system is fundamentally in memory, and there's no roundtrip to anywhere to access it or to modify it.
I suspect I’m repeatedly misreading this subthread then. My point is that web apps are still apps, no matter whether app data came from http or fs or sql. It is unclear why good old methods of slapping data controls together wouldn’t work and why is it so hard and esoteric to do fullstack today and learning curves are so steep even for a simple crud++ area.
I just thought of another critical difference when using "Qt, etc". These toolkits all have at their heart an event loop that looks something like:
while (!should_quit()) {
wait_for_user_input_or_other_events();
process_events(); // may mark some components for redraw
redraw_anything_that_needs_it();
}
So if user input or some data source indicates that some on-screen component now needs to be red with a yellow border, the "process events" stage makes a note of that, and effectively queues a re-rendering. The re-rendering takes place in the "redraw anything that needs it", at which time the code responsible for rendering can check current state and draw it red with a yellow border).
Quite a lot of what is "cool" about frameworks like React is the extent to which they are essentially trying to rebuild this fundamental event loop logic in a web app context.
It is pretty much the same, except there is no explicit loop and calls to wait for events written in JS/React code. Javascript itself runs a loop, instead of polling for events, you define events handlers, and DOM/React handles "redraw anything that needs it" part.
Just to be clear, the loop isn't explicit in a desktop toolkit either. You do not poll for events, and the toolkit handles the "redraw anything that needs it". So for example, in GTKmm (the C++ wrapper for GTK+), the event loop looks like:
Ah, it was quite a while since I looked at desktop GUI apps... It used to be just like OP described - you define logic, and then run pieces of it in the loop. It makes sense to abstract that away to standardize the process.
I'd say it's pretty correct, but I'm not sure I'd agree with what I think your conclusion might be. If you want user interface toolkits based on 20 year old paradigms in your browser, just build your app on something like YUI. :) "Make it like the desktop" was the initial wave of web developers' first instinct, and it wasn't great.
> Think about the most complex frontends you’ve used. Frontends that made you wonder — “how did they create this”? What makes these frontends complex?
State management. The frontend “knows” a lot of things, and these things interact with each other in non-trivial ways. So in my view, state management is the core problem when developing a UI. To build a non-trivial React application, you need to consider state management, in my view. For example, say your app has a dark mode. All your rendered components must know what theme is on, so they can render the UI in the right color.
This makes me wonder “how did we allow this to happen”? The beginning of this article alone reflects the whole tragedy of modern programming. When I was a child we would just use common controls and the operating system would paint them whatever way configured in the control panel. Now we have to solve a complex state management problem just to implement a dark mode. Application programming was supposed to become simpler and easier, not this...
You got that wrong.
Modern JavaScript / SPAs make it possible to implement something like Outlook right within your browser.
No (big) download, no clicking through an installation wizard, no fragmentation of versions for a tool, which forces you to be online anyway.
I don't get all the hate towards SPAs and JavaScript Frameworks here on HN. Everyone is basically complaining about complexity for applications, which are more than just a server-rendered form or message board (which is a way less complex applications compared to outlook or slack, where "realtime notification" is a requirement).
Application programming became easier by a lot, just the application requirements grew at the same speed, resulting in that feeling of stagnation.
With something like zustand[1] or the react context api, its just ~10 lines of code to store the dark-mode boolean somewhere central in the app and connect it to all the components without prop drilling.
But is React simpler for building UIs than the RAD tooling of Visual Basic, Delphi or the Flash Designer?
Obviously there is the advantage of the web platform to leverage with JS frameworks, but there was something really nice about being able to drag and drop standard controls that all users were familiar with, and then just attach some code to them that talked to a database or whatever. I think that was the Parent's point that modern web development, while having the advantages of the web, is still more complicated than what was popular a decade or two ago for making UIs.
Delphi is "textual" as well - the designer works with text files (*.dfm) that describe the component tree. Very similar to JSON, but with more Pascal-like syntax. People mostly used the designer because it was easier - but then again, in that era, it was also common for people to use e.g. Dreamweaver to write HTML.
I mean, while these graphical tools certainly help, you have to learn them. I learned things like Photoshop, Sketch, Ableton Live, Final Cut Pro, Eclipse, etc. pp. and I can tell that it's a huge time investment to get up and running.
Maybe coding is that much easier, that people invest their time into this instead of a UI builder?
Maybe people got burned by Flash and now want to invest into somthing that lasts longer?
GUI designers of the RAD era were much easier than Photoshop or Flash, and certainly easier than writing it out by hand. That's precisely why they became so popular.
The most popular frameworks with the best tooling weren't cross-platform. VB, Delphi, .NET/WinForms (and later WPF) were all Windows-only. Delphi briefly played with Kylix, but it was very messy.
Then you had Qt, which is a very Delphi-like take on GUI in C++, and was cross-platform - but it didn't have the integrated tooling on par with the other stuff, and of course the language being C++ raised the learning bar significantly.
Qt is much better these days, thanks to Qt Creator - although C++ is still a stumbling block there. But in the meantime, web apps took over - and that was before stuff like React. I don't think that had anything to do with ease of development, but rather with ease of deployment (or rather lack of it) - it's much easier to get people to use your app right there in the browser than it is to have them download and install it. So web won not because of its technical superiority, but despite it - and then dragged the desktop down (Electron etc) as a result.
I remember in the 90s there was a push for putting things on the web to avoid licensing fees and installation, despite how limited html and JS were. And there was the temporary popularity around Java applets to provide a more rich web experience prior to Flash and then finally HTML 5.
Delphi and it's opensource cousin Lazarus/Freepascal are doing just fine and I use those to implement desktop applications. Single exe with no dependencies. Beauty. Sure they're not popular in North America but I could care less. Those tools save me a boatload of time. There is also QT.
"The answers were: These tools were good for one developer or small teams, but textual dev tools scaled better to big teams."
Utter nonsense. They scale just fine. Whoever gave the answer seem to not know what he/she is talking about.
Wrong approach. The claim was made that Delphi does not scale for big teams. Name me one single Delphi related (mis)-feature that prevents it to scale.
> Everyone is basically complaining about complexity for applications, which are more than just a server-rendered form or message board (which is a way less complex applications compared to outlook or slack, where "realtime notification" is a requirement).
But we did more than just a server-rendered forms in delphi, vb and others! The hate is not for js or html (well, in this case at least). The hate is for invention of techniques which are hard to explain, follow, justify, implement and reason about.
Web had an enormous amount of seasoned and veteran developers around the world and threw this resource out the window by going a new fancy way of history repeating, which presumably no one did before and no one knew names for new methods when they appeared.
React/Redux is ST by the way, implemented in a language completely unready for that concept, but it’s unclear if its authors were aware of this at the time it was born.
Or are trying to follow fads so they're more employable (resume-driven development?) Which is a strategy I am not criticizing in the slightest. It can suck when it means the whole team has to learn the fad framework, though.
This is definitely a thing. Lots of tech choices are made for that reason, not just front end frameworks, as I'm sure you know. But there's no incentive to put one's foot down and declare that the emperor, indeed, has no clothes, so we keep praising his fashion sense and taking home our pay. This is true for developers at all levels, project managers, CTOs, and so on. Every one of them becomes more employable—easier to find the next job, next job's higher-paying—for having lead or been a member of a team using inappropriate or sub-optimal but trendy or "serious" tools. "No one got fired for choosing..." has extended to a ton of trendy crap, a fair bit of it half-baked, and you're not only safer but also better off financially than if you risk suggesting anything else, even if "standard" solutions slow down development, require more and more expensive people (that's the point, you want to be one of those!), and make your systems more fragile.
Maybe! But also, as a user, I expect to do more and more in my browser.
I would find it strange, and probably switch to a competitor, if my preferred airline for example made me install (and keep updated) a desktop app in order to book a flight.
You know one thing I expect my browser to do that it has a hard time doing these days? Efficiently and correctly displaying text documents.
It has a hard time doing this because so many web pages think they need to be web apps and overthink things instead of leveraging the tools at their disposal in the browser.
Yes, if you're building a new Outlook or Google Maps in the browser it ought to be a web app.
If you're building the remaining 90% of use cases it should probably just be a web page with boring old tech. Add JS for spice if you'd like.
Which client stack have you used where state management was simple and easy? Pretty much all desktop/mobile SDKs have all the same problems of two-way data binding that we moved away from in the web. Frankly, the web has the best experiments in state management simplicity yet that I wish we had long ago across all clients. It's taking way too long for the best ideas to trickle out into non-web clients.
macOS/iOS is a good modern example of the old way of doing things where you're stuck using this weird OOP-for-OOP's-sake Core Data abstraction that certainly doesn't feel like a local optimum. It's the polar opposite of simplicity. When things don't work, it gets complicated very fast as you try to credentialize in it.
By the way, client development was never easy. It's only gotten simpler over time. Painting a component to the screen (your example) whether you can use a built-in or have to make your own is still the easiest part of client development, not the hard part. State management and deciding on when to repaint is still the hard part. And just because the runtime gives you a solution doesn't mean it's simple or ideal. So I'm wondering which specific client development experience you're looking back on with such rose-tinted glasses.
> Now we have to solve a complex state management problem just to implement a dark mode.
What they're saying is that if you have a large complex system, it would be easier to implement dark mode with good state management. Not that you need complex state management to implement dark mode.
Author here. The way I think about it is, the web was designed for rendering static markup. JavaScript was designed for adding small amounts of interactivity.
Instead, we've managed to turn the web into a full-blown application platform. Do you think Tim Berners-Lee envisioned something like Google Maps being created in the browser?
Now, I concede that JavaScript isn't the best language for creating client-side apps. I hope someone applies some first-principle thinking and creates a language expressly designed for this purpose.
Considering how many people and developers use web apps, this could be an area where someone can have a massive impact.
What's the deal with the recommendation to use some library for managing forms? I've been working with React almost since its initial release and I've built some pretty complex forms... yet form libraries remain the one thing I've never really seen a need for.
At the very least, using something like formik shouldn't be a necessity. There should be some qualifier that it's only really needed for very complex forms. Or am I missing something?
I'll counter this by saying making forums is my least favorite thing to do in React.
Doing it by hand involves a ton of repetitive work.
Each form input needs to have its input validated client side (immediate errors), needs to have error messages from server side validation, needs to be accessible, needs to work across all browsers, any sort of drop downs from an input need to have proper a z-index, and state, so much state to track.
Doing it in vanilla Redux just sucks, and it is super easy to completely kill performance. Behold the number is sites that have a non-trivial delay when typing individual characters into a form.
(I've seen input delays on sites approach 500ms per character!)
(Of course 90% of this goes away by letting the browser handle forms, and accepting page refresh on submit, but if you want to go SPA...)
One of my larger regrets from my last project is not just learning a form library, it would've saved me literally weeks of work.
Although that entire site is a worst case, it is 3 giant forms each with over a dozens inputs varying from plain text to numeric to date-time to an image uploader and gallery manager.
Basically the best case scenario for "I should've learned a 3rd party library".
I don't think this is really a counter if you're talking about "3 giant forms each with over a dozens inputs." Yeah that sounds like a good use case to learn a library.
I guess my larger point that might not have been emphasized well enough is that recommending using a form library for _any_ form seems misguided.
I use redux-form and really enjoy it. A modern form UI has a lot of considerations that classic html/css forms do not:
* The concept of pristine/untouched inputs
* Basic and custom validations
* Resetting forms to various states
* Prepopulating data based on an API response
* Dynamically creating inputs based on UI states.
* Normalizing data upon input or submit
* Creating wizard/multi-step forms
All those implemented in Vanilla JS or in a React component can get messy. Is redux-form or any form library absolutely necessary, nope, but they sure do help.
At one place I was stuck with some React form library (can't remember the name but wouldn't shame either), as well as being stuck on a version of React Router that categorically refused to support some of the features earlier versions used to have.
I tried patching the form lib to update it from React 0.14.1 or whatever we were on way back then, to the new React 16 hotness. I eventually gave up because the code was so generic that I had to patch use-cases I'd never even thought of (good old feature creep).
Meanwhile, our home-grown forms using plain-JS, hooking into graphql, pretty much did the job with only simple abstractions. The same was to be said for the SVG-based clip-path polyfill I wrote to support IE. I had no problem writing a basic, self-contained component for that.
Most of those form use-cases are quite trivial implementations IMO and the complexity comes from working around the dependency's limited API rather than the browser itself.
Don't overthink it. Native HTML5 elements come with form validation and even a way to serialize (!) and submit form data w/ action and method attributes. (To implement the same as React components would be a 3 month project coming with a 1 GiB node_modules dir.) The only time you have to add JS bloat is for date pickers and time pickers which aren't supported on Safari yet (and you can user agent sniff for this).
Blows my mind why some people choose to adopt megabytes of dependencies when you can just write a few HTML tags and have it executed by the browser's C++ engine instead of some JavaScript nightmare.
People need way more validations than html5 elements can provide.
They need CC number validation. Validations that depend on the content of sibling elements. And they need it to display in custom ways like internationalized or whatever.
Attach event listeners and handle those parts with JS. For internationalization, template on the server side, sending a different bundle for user agents in different countries. CSS is enough to fill the gaps with other internationalization concerns like rtl.
The platform provides enough to do forms. It's one of the few things the web is good at. It has been around for ages and works. I mean, what I'm using to type this is a plain HTML form. It doesn't need a massive JavaScript framework that heats up my CPU on every keystroke.
> form libraries remain the one thing I've never really seen a need for
I felt this way too, until I used Ant Design's form component (which uses rc-form under the hood).
Form libraries take care of validation for you, allow you to specify an initialValues prop, and give you a handleFinish={values => ...} prop so it's easy to run your own API call when the user clicks "Submit".
Plus, Formik lets you model the form values using a TypeScript interface which I like.
To me, Formik provides several advantages over not using a form library, and no disadvantages except a small learning curve, so that's why I recommend it.
You certainly do not need to use a form library, but it does make it easier to deal with complicated issues such as render counts. I built a thin wrapper around material-ui and react final form (what I would suggest over formik) [1] and a little demo [2] that visually demonstrates this.
> You can make API calls in your actions just like you normally would
Jesus don't do this. Mobx, Vuex, Redux, etc, are about managing application state not about managing application logic.
I started making SPAs in 2015 and I also used actions for everything (API calls, auth, etc). It was fine for small projects but then I got to work on a medium sized project and those actions became huge and controlled everything in my app. I usually consider myself a pragmatic person but that was impractical for a number of reasons and also conceptually wrong.
When working in SPAs now I remove anything that is not state management from MobX/Vuex/etc (I don't use Redux anymore). That logic belongs somewhere else.
The pattern I'm doing now is that I conceptually separate components from the application logic. The store(s) feed data to the components but if the components want to interact with the application they go through a mediator of sorts (eg: changing a route, modifying state, authenticating, etc). This mediator then interacts and coordinates with the different application modules.
For example when authenticating many things have to happen in different modules (API, store, router):
1) An API call has to be made to verify credentials and receive a token
2) This token has be stored somewhere eg: localStorage
3) Maybe we also need to make an API call to get user info, their roles, etc
4) Some state in the store has to be modified. Not only the user info, but the app logic usually needs to know if a user is authenticated.
5) We need to tell the router to go to somewhere eg: /home
It makes no sense to do all that in an action. Store actions should be about managing state, period.
What does "do in action" mean? If you mean "make API call in reducer", that is commonly known to be an anti-pattern. Triggering the API call by dispatching action, however, is a common pattern. Usually it's implemented with a library like redux-saga or redux-observable.
I agree it's better if the implementation of, say, the API is not done in actions.
Although I do not agree actions should be responsible for triggering application logic. It is a very popular pattern indeed, and I've used myself a number of times, but my code makes a lot more sense now that store(s) are only responsible for state management.
I think this is generally a side effect of people considering actions to be a replacement for the Controller part of MVC. Meaning any sort of logic (including legitimate state manipulation) is jammed into actions.
Personally, I don't see that as a huge problem. Better in an action than sprinkled into components. As someone else mentioned, redux-thunk makes this even easier (and one could argue, promotes this).
I haven't used it but AFAIK redux-thunk allows you add async middleware to redux. I don't see how adding more features to your store would alleviate the architectural problems I've mentioned. If anything it makes you store even a bigger protagonist in your application.
I'm starting to lean towards holding ALL app state in a single store rather than having component local state because every so often I get burned by not seeing the future.
Example: okay I have a bunch of tabs that show different views. The state for which tab is selected can live with the tab container. Months later I find that I need other parts of the app to be able to switch to a different tab when user actions happen.
At this stage all I put in local state are the most basic things like dropdown open/closed state.
If you're looking to retrofit something like this in, just for a simple piece of state like which tab is active, and don't want to refactor all your existing local state management code, Context is a really powerful tool for this.
I think of it like a dedicated state 'portal' - should I need to use one particular piece of local state in other components, I just write a quick dedicated Context for that one piece of data (super quick and simple and React-y using the new API) and simply set the context as well whenever I set that piece of state.
Won't work for everything, is a little hacky in a way and you wouldn't do it that way from scratch, but it's such a quick and easy solution to this issue compared to a major redesign of the global/local state.
Agreed, I use Context as a "store for UI stuff" and it always fitted perfectly. Keep data in the global store (Redux or whatever), component-specific state in the local state and inter-component state in Context.
And I wrap almost everything in hooks anyway, so the component doesn't care if it's local, Context or Redux: it's just data and I'm free to move it in different stores.
Tabs are typically the sort of thing that makes me think "context" immediately. Also implemented nested collapsable sections with this, TikTok-like feed where different components control the same UI component (sliding panel), tables...
I never find myself "burned" by locally stored state because it's trivial to move state up the tree and pass it down as props. But boy does it simplify an application a lot using locally stored state where it makes sense.
This is the way Elm works and I believe some people used or advocated for using Redux in this way early on. It never really caught on in React apps in my experience but it makes some interesting debugging methods easy to implement.
This is also how it works in Clojurescript (through Reagent/Re-frame). I don't have too much JS react experience, but I often read this kind of article and marvel at how huge the mental load is working on react projects in native JS.
You can try this, but I don't think it'll turn out well!
You don't want to have a bloated, disorganized global store...what you're doing now, moving state into global state only if needed, is the right way to go.
Having everything in global state is cheap if you use Immutable.js data structures. In my experience, Immutable.js the only way to do the global store pattern e.g. Redux without making your app slow to a crawl.
Note that we specifically recommend _against_ using Immutable.js, and that there's many misunderstandings about the hypothetical performance benefits that Immutable.js provides:
I didn't know about this new library but from my benchmarks it was a night and day difference moving to Immutable data structures all the way down. I understand not wanting to recommend it to newbies, but it's actually good.
If you can take a few minutes to read through https://redux.js.org/style-guide/style-guide#use-plain-javas... , and specifically the "Detailed Explanation" section there, I talked through all the perceived and actual perf benefits that you see with Immutable.js.
In short, most of the benefits people _think_ they get from Immutable.js are just properties of immutable updates in general, and there's actually some pitfalls of using it. The only real unique benefit is faster copies of _very_ large objects.
I'm not saying it's not helpful, just that it's not a silver bullet in and of itself the way some folks think it is, and that you can get similar benefits without the overhead of its API.
To manage state in our React application, we have a "service layer", just like you’d see in a server side application, only these are based on state machines (reactive services based on RxJS observables works as well).
We also created associated custom hooks that allow developers to easily "inject" a service into their React component (we use React's Context API for our "DI container").
From there developers can read the service state to drive the look of the UI, or send events to it to trigger behaviour that is encapsulated in the service itself.
Our most used service is a UserSession service that manages OAuth based login flow and session management.
The best part of this approach is that our service layer started in an Angular based proof-of-concept before we lifted-and-shifted it all into React.
I wouldn't call it the "best part." It sounds like you took the service focused angular patterns you were using before and shoehorned them into a React application.
How comfortable would you be allowing an outside dev to hack on your codebase without a _long_ conversation beforehand explaining the idiosyncrasies?
From my view point, we “shoe-horned” a React UI into a well-established pattern for building an application.
I guess it depends on the vintage of the developer. I would argue that Redux is an idiosyncratic technique for state management, and by breaking it apart into separate services, you get better separation of concerns.
I should point out though, that our “service layer” follows the same event-driven flow that you would see with Redux, but by breaking it up into state machines we can be more explicit about the states of our application.
“Service layer” doesn’t have to be a dirty word. Redux is a stateful, reactive service, and so are the xState machines we are using. Logically speaking, I don’t see much of a difference.
As for the _long_ conversation you mention, in my experience, it’s actually been a pretty short conversation followed by gratitude at not having to use Redux. I really don’t meant that as a slight towards Redux, which is a very elegant solution, but seriously, the people I’ve introduced to xState just like it better.
Just xState. Each machine is responsible it’s own piece of the global state pie by using its internal `context`.
This allows a similar approach to redux’s actions and reducers (or events and `assign` for xState), but each machine can focus on its area of responsibility and enforce its behaviour through defined state transitions.
This approach also makes it easy to coordinate between multiple services (e.g. having the “NavigationService” change which links should be available by listening to a state change on the “UserSession” service as a user becomes authenticated.
This started as a bit of an experiment to see what a stateful UI “service layer” might look like, but as we’ve progressed, we haven’t found a need for anything else like redux or mobx.
> e.g. having the “NavigationService” change which links should be available by listening to a state change on the “UserSession” service as a user becomes authenticated.
Could you expand on how you're achieving this? I'm creating a new project using xState at work and this is one of the things I've been trying to come up with a solution too.
My biggest problem is the pattern I've set up for routing on top of xState (It's an electron app so no URL management required which makes that easy) requires you to create the state machine yourself, so it can't for example be used as an actor in part of a larger system.
It sounds like your NavigationService and UserSession service might be similarly separated so I'm curious how they're communicating?
Something that occurred to me last night is that I can probably make a coordinator of some sort somewhere in the tree which forwards actions from state machine to another as required.
We keep things very simple. Maybe that will have to change as the code base grows, but for now it works and it easy to grok.
On start of the application we create singletons of our state machine backed services, and manage the dependencies between them manually. That is, we create an instance of the UserSessionService and then pass it into a "constructor" for the NavService. That constructing function creates the NavService and then wires them together by subscribing the NavService to listen to the state transitions of the UserSessionService.
I suppose if the code base grows and you have dozens of services to keep track of, it will get pretty unwieldy, but that is no different than using a fancy IOC container in Java/.Net. We plan on keeping things simple, while still braking apart the single store you'd have with Redux.
I hacked together a simple gist to show the basics of how we wire things together (without getting into the specifics of how the internals of the state machines work). [1]
That does help clarify things, thanks :) For context I'm just starting out this app so it's simple for now, but this is my first major project with xState so I'm trying to come up with sensible patterns.
I'll have a think about this and see if I can work it into my structure as I'm certainly going to need inter-machine communication as it grows.
It also shows my prototype for xstate based declarative data fetching, though I have sinced switched to using observables instead of promises for representing my GraphQL query results so I've changed it quite a bit.
This post could more strongly emphasise something important to understand about writing ReactJS applications - taking a declarative approach to the overall application architecture.
Trying to write React applications in an imperative manner (the more traditional approach to programming as opposed to declarative), twisted my head in knots and made things harder and more complex than they needed to be.
What I mean by writing React applications in a declarative manner is:
1: you have a global state in your application
2: the global state includes an object with information about what state the application is in, for example:
4: components decide to display themselves by querying the global state
It becomes easy then for your buttons and links to simply adjust the global state to make things happen. Because React rerenders the application each time the global state changes, you need do nothing explicit (imperative) to drive the application - it all just happens automatically as a result of what actions the user takes - actions that alter the global state.
React hooks and context make this approach very easy and clean to implement/
That's the way I do it anyway - an approach I find makes the entire application easier to understand.
I think the largest myth that exists in the react world is the need to have a global state for everything. I've seen too many failures when teams go from displaying "this one thing" to suddenly have the requirements to display two or more of that at the same time. Redux global state for the user that is logged in? that's OK, but do you really need Redux for that?
Our team uses hooks and context for everything, and a little bit of legacy redux usage.
You don't need to put the username of the logged in user into Redux, but if multiple components across different pages need to access this data, you should make it globally accessible.
You can choose Redux/MobX for this, or you can roll your own solution with context and hooks.
I'm actually learning more about the context/hooks approach myself.
Kent C Dodd wrote a great article on this topic I'd recommend:
Then try to simplify the app as much as you can. A system has always 2 kind of complexity: the intrinsic complexity and incidental complexity. "Smart engineers" like to show off their intelligence. That usually end up making their life harder by adding a lot of incidental complexity that eventually lead to fragile architecture and frameworks that add more complexity than necessary.
I do not like Redux a lot. It leads to a lot of boilerplate code and a lot of extra files. It also exposes transparently the state that lead by default to a coupling between the "internal" data rappresentation and the rest of the UI components making refactoring a nightmare.
Mobx at least leads to a more concise codebase.
GraphQL and Relay/Apollo/etc help a lot to reduce the business logic in the front and code to written in general.
Why is state management such a talked-about issue with React? Other than distributed state, which obviously comes with its own set of challenges, managing state doesn't seem to be an issue with any other language, framework or platform. But with React, it seems to be a major part of the learning curve, with entire tutorials dedicated to it and numerous libraries to help with it in some way.
Because React, more so than the other frameworks, puts such a strong emphasis on "the UI output is entirely based on your state", while at the same time there are some limitations to tracking state in ways that are inherently tied to your UI hierarchy.
If you look back at a typical 2011-ish jQuery app, the exact opposite is true.
You might have a modal visible in the page, but the only indication code-wise that the modal existed was in the DOM itself, because there was a one-time mutation of the DOM via `$("#my-modal").dialog()`. The only way any part of the app could _know_ if that modal was being shown was to check on the actual DOM itself, like `$("#my-modal").is(":visible")`. Then you start getting into weird bugs because maybe some other script has mutated the portion of the DOM you cared about, or you're having to write logic to handle if the DOM _was_ in this state and now needs to be in _that_ state.
What we've learned over the last decade or so is that while that is fine for adding small amounts of interactivity to the page, it's not a maintainable way to build large-scale application codebases.
Instead, you want to have an actual `isModalVisible : true` boolean be the real source of truth, and the UI should just be a reflection of that value.
I'm not saying that React invented this idea or that it's exclusive to React, just that React's docs and community has generally put a stronger emphasis on that specific mental approach to designing the app:
What they're saying is that there was no clear separation between model view and controller. Now they've really embraced view (React) as a pure function of model (Redux) that has very clear way that events modify state coming from the controller (your business logic).
> Why is state management such a talked-about issue with React?
The "state problem" that exists in React, Vue, Flutter etc. is due to their maintaining components in a hierarchical tree. It is a manifestation of a "cost" (drawback) in that tree-based approach.
React represents elements as encapsulated components that can be composed into more complex elements, which are maintained in a hierarchical tree.
That's the main attraction. One can reason about independent pieces representing some aspect of UI. Those pieces can be building blocks used to build more complex things.
The price for this is that how these (encapsulated) components communicate and share data between them is problematic because of that hierarchical tree structure, where an element only knows its parent and child(ren).
Because managing state is really the major unsolved problem in front-end. No framework has it really solved -- but in React (and Vue) the other stuff is so easy that the issue of state management because much more pronounced.
See also: Reagent. State is a first-class construct in Clojure/script, and forms associated with statefulness and state management are directly available. From that perspective, none of the front-ends presented appear complex.
> For example, say your app has a dark mode. All your rendered components must know what theme is on, so they can render the UI in the right color.
Wait, what? Hell no. This is literally the core use case for CSS classes. And while there are cases where CSS classes can cause problems, this isn't one of them. This is a case where CSS classes shine.
This is like an article on how to use fire to solve problems, and instead of choosing an example like staying warm or cooking food, the author leads with an example like disposing of tires by burning them in the rainforest. It's just an unambiguously bad idea.
There were a bunch of places where I wasn't familiar with the tools he's using and am curious, but I'm certainly suspicious of anything he suggests after the "use global state to reinvent CSS classes via JS" suggestion.
Yeah, I have a stylesheet for layout and then one for each color scheme. Changing the scheme is literally changing the class attribute of the body tag.
I know nothing about state management on frontend, but please make supporting "history back", "history forward" and "F5 reload" use cases easier. It seems to be a hard problem to crack, given how often these simple actions are broken
It's really not a hard problem to crack. In most cases where history navigation doesn't work it's due to nothing more than oversight. It'll generally just be someone doing a JS route transition in an onclick instead of just making a JS routing-aware link with an href.
There are some cases where history is a bit trickier to get right, like infinite scrolling. Some apps have it working, although with varying levels of success in my opinion. The forum software Discourse does a pretty good job: https://discuss.emberjs.com/t/is-the-six-week-release-cycle-...
I’ve been learning React and have been avoiding Redux out of fear of complexity. However, as my app grows more complex, the need for a global state manager becomes more apparent. Time to start learning Redux today :)
1) Redux is pretty simple, but some of the terminology is obtuse for no good reason and makes it harder to get a grip on than it should be. "Action" = event, "Action Creator" = any function that emits an event, it's basically... not even a real thing worth discussing with its own special term. There, problem mostly solved.
2) TypeScript is the only sane way to use it. The bouncing between files and "wait, what was the shape of my state here?" crap is intolerable without it. I mean that's broadly true of all Javascript but it's very noticeable with Redux. This is doubly true if you're working on a team.
3) I've had a lot of luck abstracting Redux behind a broader client library of some sort. You can expose the redux store itself and let the using code "compose" it as usual, but stick most of the "action creator" stuff behind the wall of the library, minimizing and moving to a more suitable level of abstraction the surface area the calling code is exposed to. This is especially nice if you may use the code in some environments where you don't want to or can't practically use React—you can still easily re-use the Redux portions, which is wonderful. Also tends to leave you with state code that's easier to test in isolation.
4) One of the big limitations of it is that you can't reference one part of your state from another. You have to copy, because of how Redux automagically detects changes to the state tree. This has been a lot more annoying and limiting than I thought it would be at first—turns out I need/want to do that more often than I thought I did, and the more complex the app the more I want to do it—but you've either got to learn to live with it or have some kind of external, supplemental state management alongside Redux.
Nailed it. Not only do I share the same sentiments, I have also had a very similar experience with 3.
Specifically, I like to be able to interact/test with my application as I build it in the console. I have had much success with wrapping my "Action Creators" in a API of sorts and mounting it to the `window`. This saves me from having to either create some sort of one-off component to "get at" some piece of functionality or hand-rolling actions in redux dev tools.
I've done similar things, and yeah, it's great for making quick-n-dirty utilities to poke around with your data & state handling code. You can even throw ugly non-React GUIs on top, trivially, including in something like Electron (if, say, your application needs access to functionality or modules not available on the Web, as could be the case for certain Javascript deployment targets like mobile, set-top boxes, desktop, server, and so on).
Understanding the original context and intent of a tool is important. Back in 2017, I wrote a post that covered the overall intent behind Redux's design [0], as I was already seeing that folks didn't know the background. That lack of understanding behind Redux's history has become more apparent over time.
The terminology exists because Redux was originally designed as "just" another implementation of the Flux Architecture. Both "actions" and "action creators" were terms that had already been introduced by Flux [1]. There was considerable debate during the initial design phase about what terms to use, and the conclusion was to stick with Flux terminology to match the target audience of the time [2].
Note that the new Redux Style Guide docs page specifically recommends "modeling actions as 'events'" [3], using the "ducks" pattern for single-file Redux logic [4], and writing Redux logic using TypeScript [5].
In addition, our new official Redux Toolkit package [6] is our recommended approach for writing Redux logic. It has several utilities for common use cases like setting up the store, writing immutable updates, and creating slices of state, and it's written in TypeScript with an API designed to minimize the amount of type declarations you have to write. (In fact, I've even used its `createSlice()` function to write some fairly complex reducer logic that I used with React's `useReducer` hook.)
Finally, you _can_ reference multiple parts of the state tree in your reducer logic, but you have to explicitly set that up yourself [7]. The `combineReducers` utility is meant for the standard use case of defining update logic by domain "slices", and it's up to you to write custom logic if you need more specific behavior than that.
Yeah, I know the history of the terms, I just find them so entirely unhelpful for (harmful to, in fact) understanding Redux, no matter what reason they're there, that providing a translation is pretty much the first thing I do when introducing someone to it. Figuring out what the terms actually meant was the moment I went from "what... what is this thing doing?" to "oh it's a couple very simple things I already understand, no big deal, got it now".
> Finally, you _can_ reference multiple parts of the state tree in your reducer logic, but you have to explicitly set that up yourself [6]. The `combineReducers` utility is meant for the standard use case of defining update logic by domain "slices", and it's up to you to write custom logic if you need more specific behavior than that.
As I've already said in this thread, we're currently working on a docs rewrite to try to update things for today's target audience: https://github.com/reduxjs/redux/issues/3592 .
As part of that, we'll be adding better explanations of terminology.
(and by "we" I mostly mean "me", since I'm not getting a lot of help from the community with this task atm.)
Thanks for your work on it. Seriously. I've enjoyed my time with Redux, more or less, and was in no way trying to shit on it with my comments—I hope it didn't come across that way.
Try Redux; acemarke has been evangelizing Redux Toolkit [1] recently and it looks great for a beginner not familiar to the Redux ecosystem.
But also - if Redux just isn't clicking for you, or if it is but you feel like writing it is unnatural - know that you're not alone.
Learn MobX, learn how to manage state with context and hooks [2], learn Apollo Client.
Yes it's a lot of up-front work, but you really do need to find a state management approach that works for you if you want to write non-trivial apps, in my opinion.
>I’ve been learning React and have been avoiding Redux out of fear of complexity.
Redux seems to be very polarizing about whether it increases or reduces complexity. As a back-end weenie who finds virtually everything about the front end confusing, I found Redux to be a breath of fresh air. Finally something straightforward that made sense and where the complexity of the code never seemed to exceed the complexity of the problem it was solving. But I was working with people who could run rings around me in the big React codebase we were working on who said Redux was a total mindfuck for them and they would never use it again if they had a choice.
It's not the amount of complexity that leads to needing Redux, it's the type of complexity. You can go a long way on keeping state in the nearest common ancestor of the components that need to share it.
Also, please check out our new official Redux Toolkit package. It includes utilities to simplify several common Redux use cases, including store setup, defining reducers, immutable update logic, and even creating entire "slices" of state at once. It's our recommended way to write Redux logic:
Meanwhile, we're working on a major rewrite of the Redux core docs. I'm hoping to put together a new "Quick Start" tutorial page that will show how to use Redux Toolkit and React-Redux in a "top-down" approach as a fast way to get productive. The current tutorials take a "bottom-up" approach, teaching how Redux works from first principles. I also want to rewrite those, keeping the same teaching flow, but updating the content to be easier to understand and to teach patterns that result in simpler code.
Check out Statium: https://github.com/riptano/statium, and the RealWorld example that I threw together in a couple of evenings: https://github.com/nohuhu/react-statium-realworld-example-ap.... The basic idea is to have state kept in a separate component that deals just with state, a ViewModel. Internally it works just the same as usual React components: by calling `this.setState()`; externally it exposes API for getting key values and setter functions, same as hooks do.
It's really easy to work with, and the concept of hierarchical state store is something that I haven't seen in any other library. If there was I probably wouldn't end up writing Statium in the first place. :)
Check out mobx-state-tree ...it's mobx on steroids, and it provides what is missing from mobx to make it a state management solution for large react apps (minus the boilerplate like you get with things like Redux).
I think that plays along the lines of Rich Hickey's simple vs easy idea. Sure, a beginner will understand your explanation, but they might still run into trouble doing common UI things using it.
Redux also has a reputation for being annoying to write because people cargo cult it into any project regardless of scope and because utilities like the excellent `redux-toolkit` weren't and still aren't as widespread as they should be.
And the growth of the RTK package is especially impressive given that we renamed it from "Redux Starter Kit" to "Redux Toolkit" _after_ we'd released RSK 1.0.
We'll emphasize RTK even more as part of the ongoing Redux core docs rewrite. My goal is that it ultimately becomes the default way to write Redux logic, in much the way that the Apollo folks tell you to use `apollo-boost` and React devs default to use Create-React-App.
Help me out here. So state management refers to frontend tools like Redux and Vuex, and it sort of represents the "backend of the frontend" in that it defines constructs and their behaviors for the frontend to manipulate. Is that a correct understanding? If so then doesn't it result in some bad code duplication between e.g. the Redux layer and the Django models?
Not sure what you mean by "bad code duplication"? If you have a need for a rich front end application you're going to need to implement some state management on the front end. The back end is responsible for running APIs, managing services, interacting with the database, and performing validation on API calls. A good REST API is typically stateless. But a front end can't be stateless. Let's take Facebook for example. The front end needs to track which conversations are open, which users in your friend list are "active" or "away" and render the appropriate icon, threads of conversations need to be woven together on various panes on the screen, visual feedback about whether a post has already been "liked" by the current user needs to be provided, etc. It needs to be able to receive real-time updates and post those to the UI in the proper location. There's a ton of complexity there and you have to place that state in memory on the client somewhere. Managing front end state effectively is crucial for complex applications.
That's pretty much it. The Redux store is a database (up to you to keep it normalized), things like selectors and actions are akeen to controllers (one just reads data, the other just writes).
You do have some "concept duplication", more than code duplication. Indeed you'll have to define your interfaces twice (Django models and Typescript definitions for example), but that's always the case if you consume an API (you always hope that what you expect from an API is what is received).
Yes, that's why a lot of people who do SPAs also run node as the backend so that business logic code can be shared. If you have an e.g. C++ backend, you will have to rewrite your models in JavaScript and keep them in sync.
It makes your website have the same development tedium as a mobile app but without any of the prestige or profit potential. Avoid SPAs at all costs. More companies are starting to ditch them and only have websites as advertisements for the native mobile app (CashApp and Venmo off the top of my head).
May I ask React guys what’s wrong with parallel controller tree and (virtual) view tree which React tries to avoid (and, in my opinion, creates a bunch of in-club issues on a way that didn’t exist outside of its paradigm)?
I’m talking about this desktop mvc adapted to hyperscript approach:
class AppCR extends CR {
ctor() {
this.db = new RemoteDB
this.sidebar = new SidebarCR
this.content = new ContentCR(db)
this.val = {a:42, b:3.14, c:this.db.fetchC()}
}
render() {
let {h, val} = this
return h.div.wrapper(null, [
this.sidebar.render(),
this.content.render(),
h(AuxViewType1),
h(AuxViewType2.model(this.val, "c")),
h.div(val.b),
])
}
}
class AuxViewType1 extends View {
render() {
let {h, cr} = this
return h.span(cr.val.a)
}
}
class AuxViewType2 extends View {
render() {
let {h, model} = this
return h.model_input({model})
// model == {object, key} (for "input")
}
}
Your controllers localize data, speak to the model in a non-ambigous way, provide data for views directly (no props, think view.cr and view.model works at any depth) and integrate other controllers by creating/destroying them explicitly and rendering when they are needed.
You also may have a global read-only (mostly) store of reference data, but that’s more for caching large datasets, not for state sharing.
Btw, model_input could wait on a val.c promise and rerender appropriately without your intervention (something React folks were to deliver last year, supposedly with help from async setstate process, which was a requirement for that (why?)).
All of this React thing honestly feels like Haskell sneaked into an enterprise for the sake of “so cool we did it” giggles. Dan’s comments on this did not help much when I read ‘em.
Point 2 in this article says a global store is the solution to its example problem. If there are any details of the example problem that make a global store the best solution, those details are not given. Generally you store a component's state in the component, and store shared state in a common ancestor of the components that share it.
With the release of React Hooks and the advent of GraphQL + its client-side libraries, state management [0] became a blessing IMO. Whenever it's possible I would opt-in using GraphQL for client-server communication. Then popular libraries like Apollo Client help you with all the state management for remote data and its caching. After this, all the remaining state management can be done with React Hooks (useState, useReducer, useContext)[1]. Only if this gets out of hands, opt-in Redux/MobX/...
I am just a regular C/C++ developer. When I read OP's article, I was dismayed by the number of tools or frameworks he refers to. I wish to become a web/app developer. How do I climb such a steep curve? Can someone please suggest a good online course.
I used redux before switching to Apollo and a query parameters / routing based state management. In Web Applications especially, it makes sense to couple global state to URLs. It makes the page and the current state shareable and brings value (like tabs, filters, pagination etc.). I developed a library based on zustand and immer for this case (called Geschichte). It’s open source https://github.com/BowlingX/geschichte
Most of the use cases I can solve with that, the rest is usually either forms (covered by formik, final-form etc), async loading of data (Apollo) or local state and animations.
Didn’t catch any mention of hooks in this article. My advice is only use redux when two components need access to the same information within a few seconds of whatever you are doing.
Otherwise I’d put all data fetching into hooks which can give any component which uses it access to all kinds of information and the ability to conditionally render.
I have found this pattern to be cleaner than using component lifecycle methods. An example can be seen here:
Ok, only tangentially related. I would appreciate anyone more knowledgeable than me in React chipping in: Why Redux is the norm for React state management?
Follow-up specific question: In which cases is it better than react-easy-state[0]?
I am using react-easy-state on my company and on my side-project web apps. For me at least, it seems to add much less cognitive overhead than Redux. It is simpler to understand, simpler to use, simpler to read and maintain.
Am I missing anything? Because react-easy-state seems very underrated in my opinion.
I'm a Redux maintainer. There's a bunch of reasons why Redux took off:
- In 2014, a year after introducing React, Facebook announced the "Flux Architecture" concept [0]. Since it was mostly just a pattern and not a library, over the next year the community created dozens of competing Flux-style libraries, which I've referred to as "The Flux Wars" [1].
- Redux came out in the middle of 2015. Its design took inspiration from many of the existing Flux libraries, but then added some Functional Programming principles on top of that [2]. Many React devs concluded that Redux was the better Flux implementation, and several of the other Flux library authors promptly stopped working on their libs and began recommending Redux.
- Redux was designed to work well with React from the beginning, including the design of the React-Redux API [3]. Among other things, React-Redux made use of what was then a little-known React feature called "context", which enabled users to avoid prop-drilling values all the way down the component tree. In addition, the initial sales pitch concepts of "time-travel debugging" and "predictable state updates" appealed to many developers who had seen problems with libraries like Angular and Backbone.
- Along with all that, Dan Abramov had already picked up a bit of a reputation in the React community from his work on things like React-DND and some Flux-related blog posts.
Once Redux hit an initial critical mass, it became self-sustaining. People in the community assumed that if you were going to use React, you _had_ to use Redux. Tutorials were written that taught both of them together (along with setting up Webpack and Babel from scratch). Now you've got bootcamps teaching beginners React and Redux at the same time (which is unfortunate, because we recommend that people should focus on learning React first, and only tackle Redux once they're already comfortable with React).
Today, there's certainly plenty of other good options out there, with varying tradeoffs. My own estimates are that around 50% of all React apps do use Redux, and overall absolute usage is continuing to grow [4].
More recently, our new official Redux Toolkit package has been designed to simplify many common Redux usage patterns, and is now our recommended approach for writing Redux logic [5].
Sorry, I have no experience with Flutter or knowledge of how Redux is being used with it, other than having seen one or two random comments where the two names appeared together.
Thanks anyhow. I think part of the difference is javascript doesn't have streams, but Dart does. And that it's recommended to use the tools built in to the lang, but there must be more to it.
Hi - author here. I haven't used react-easy-state unfortunately. But like when building a company, the best product doesn't always win: the company that executes the best does.
Executing well includes building a great product, but it also requires marketing, which the Redux community has done a great job of to the point where it's the de facto solution.
Bounced around 2 projects in the last 12 months, one Angular and one React, that are using the Flux pattern. It is a real bitch to unit test anything but the individual pieces, which ends up being kind of meaningless.
I love React, but this (and forms) is the bane of my life. I've started to unit-test my frontend a lot, lot less and rely on a lot of e2e tests. Cypress is great and I trust my app more to actually work when a user uses it
Debatable but I get your point. The thing is that Flux is a pattern, and you often want to test the full pattern to get a meaningful "unit" of behavior/logic. There can be meaningful logic in the individual pieces, but frequently they only make sense as a whole.
From that Facebook article: Flux eschews MVC in favor of a unidirectional data flow.
This is so incorrect. MVC doesn't imply two-way data binding. The whole Flux architecture was based on a misunderstanding of MVC, and is therefore questionable.
Most UI frameworks, including iOS, ASP.NET Core, JSP and JSF (Java based frameworks), Ruby on Rails, and Django (Python) are all based on MVC.
Why is it that MVC works for all these other frameworks, but doesn't for React? The answer is that MVC does in fact work for React. React itself originally used the tagline "React is the V in MVC". Flux/Redux introduce needless complexity and unnecessary boilerplate. There are MVC libs that make React much easier to use.
> iOS, ASP.NET Core, JSP and JSF (Java based frameworks), Ruby on Rails, and Django (Python) are all based on MVC.
Don't all of those have two-way data binding?
> Why is it that MVC works for all these other frameworks, but doesn't for React?
I think the point is that Facebook found it doesn't actually work well, at least for their use cases. They found a recurring set of bugs caused by two-way data flow, and built a framework to alleviate it.
> I think the point is that Facebook found it doesn't actually work well, at least for their use cases. They found a recurring set of bugs caused by two-way data flow, and built a framework to alleviate it.
I haven't seen much mention of Apollo here. It offers a 'full stack' state management solution from server to client to offline cache. Each component can be adopted independently, but they work really nicely together. The tradeoff is that you're writing graphql, but it comes along with a lot of benefits, like consolidating requests etc. On the server-side, graphql can be a thin wrapper over rest apis. Defining good schemas can be a bit of a pain, but they maintain a level of consistency throughout the system as it grows.
Hi - author here. Thanks for mentioning Apollo. I've actually abandoned maintaining any data state in my global stores. I just use Apollo and provide a refetchQueries to my useMutation call to update my UI after mutations.
Great article! I have only done a few simple projects with React, except for one where we used Redux. For me, Redux is too much clutter. I can recommend a look at this article: https://medium.com/@yiyisun/redux-vs-the-react-context-api-v...
I've been working on an AppRun based hobby project for a couple of months now, and I find state management there to be quite easy. No clutter!
I'm in the process of building a B2B multi-tenant SaaS application and one of the best decisions I made so far was to use Easy Peasy [0] to handle the state of my React application logic. It has a very intuitive and powerful API. I would recommend everyone to take it a look as it is not very known.
> URL state: the route the user is on now. Read and update window.location; don’t create a second source of truth
I've seen so much back and forth on this issue during my time developing with React. Intuitively, it seems like the URL should be treated the same way that DOM elements are: as a source of actions that may or may not result in state change - in addition to a reflection of state.
I have abandoned all of the JavaScript state management libraries and now prefer to simply write my own storage module using Meiosis[1] and a tiny library for operating streams. This replaces tech debt and repetitious boilerplate code with simple, meaningful and functional code.
This post is overly complex in its suggestion of libraries. He does have a good point of understanding different kinds of state and that state management in front end is perhaps the hardest problem. If you think about it well earlier, a lot of things become easier as you scale.
I agree that libraries for certain things, like MobX/Redux or Formik, may be overkill.
What's more important, though, is that people choose an appropriate equivalent to these libraries. Replace the library, but not the method of organizing state.
For example, feel free to replace MobX/Redux in my post with your own global state management solution. But I recommend replacing it with a global state management solution, not something else.
For anyone not familiar with UI app design, you do not need to always default to a Redux or Mobx. I've found a simple object / class with an event emitter will suffice for many cases.
> but you do need to figure out how to store global state that can be accessed anywhere in your application.
Immediately I disagree and feel antagonistic towards this article.
Then the author provides an example of "prop drilling". Sounds an awful lot like Inversion of Control [1]. I've been down this road before. Next comes a Services pattern [2] (the world re-invents COM once again) and then comes Dependency Injection [3]. What he calls "global state" others have called "cross-cutting concerns" [4]. Pretty soon we'll be all aspect-oriented-programming [5] up in here.
> What you need to do is store your theme setting in a Redux or MobX store, or in a plain JavaScript object, and pass it to all your components using Context.
Yup. The beginning of a services pattern by changing to inversion of control. Once you realize you don't want all of your state on a single object you'll have `context.getUser` and `context.getTheme` and then you'll think having specific functions is a pain so you'll have `context.get(ContextType.User)`. Then you'll have interfaces so you can easily unit test with mock services.
This is all good stuff. Keep going down that road.
No :( unfortunately it's more of a library than a framework, and there's no broad consensus on how it should be used (although there are certainly some opinions floating about on that front)
Ok; ignore that specific example -- if you needed to share a piece of state among many components in different places in your component tree, how would you do it?
> What makes these frontends complex? State management. The frontend “knows” a lot of things, and these things interact with each other in non-trivial ways [...] Example: dark mode support. For example, say your app has a dark mode. All your rendered components must know what theme is on, so they can render the UI in the right color.
Back in my day.... you'd slap a class on the <body> tag, and you'd have all you'd need for your CSS to style the rest of the page accordingly. How is using Redux an improvement here?
None of the examples of "complex" front-ends would be at all difficult in an old-school "just re-render the page" setup using something like pjax or turbolinks. And in my experience (seeing web pages with > 1MB js files) it'd be faster/more responsive for the end-user too.