React I love you, but you're bringing me down
marmelab.com
marmelab.com
The frontend just collects the cruft of a product organization changing course very frequently. There are always a dozen half-finished fix-the-world ideas conflicting with each other, half-finished move-to-new-framework initiatives.
I just mean to say that when I hear "it's because React" or "it's because rails views", any other of the infinite variations of this, I kind of just tune out. Some part of your organizations chaos is going to reflect in code, and honestly I'd rather it be in a big ball of frontend than in the data models, infrastructure, etc.
I disagree. I like my frontend to be as dumb as possible. It gets data and reacts to it as simple as possible. Seen too many frontends with a lot of data logic and it becomes extremely brittle, harder to test.
These are the ones where the frontend devs are getting data from the API and creating new structures, transforming it, conditionals everywhere, etc.
It requires way more discipline to ensure that frontend devs aren't recreating the wheel and writing duplicate code as the app and components evolve, and it's worse for larger codebases of course.
They need to build some new component and go - ok I got the data from the API and now I need to do the thing that's already been done by another frontend dev for another component because we've allowed the frontend to be responsible for transforming the data. But if they aren't aware of this, then you get a lot of dupe code.
Just have your backend API provide the contract. Setup resources/transformers as needed on the backend instead of littering the frontend with that logic.
What I mean is that even if you've followed a specific set of programming principals, code practices and design patterns consistently across the life time of the application, there will still have been initiatives to change how things are done. Maybe they want to use a "better" authentication library, or add a material framework that has capabilities they need. The exact initiative, and how valid the request or decision is doesn't matter. These initiatives may occur one at a time, or be competing with each other for priority while being incompatible without anyone noticing. But some of them will fail to be completed, and someone has to back out the changes that shouldn't go to prod. Inevitably something will get left behind. Over 5-10 years this can have a shockingly large impact on the code base in ways engineers frequently notice but don't understand how the code got to the state its in.
This frequently means dead code, unused dependencies, comments that don't make any sense, properties on objects that are never displayed or referenced by business logic, and multiple classes/services which accept the same inputs and produce the same outputs but do the job just differently enough you can't be sure it's the same. That, and over abstractions (any piece of code which branches based on the context of "who's asking").
And frankly few things any me more than someone who felt the need to implement their own logging implementation, especially in C# (yes I know we're talking about React, but people do the same stupid crap in that case as well). Reinventing the wheel has to be done on occasion, but you need to ask yourself if it has to be you that makes it happen,and whether it already has been.
People get promoted for all the shiny new things they do, not for the discipline they show in only focusing on what really matters.
customElements.define('my-element', class MyElement extends HTMLElement { ... })
I can manage state within the component, app state in window.state. Coding up a simple reactivity is really pretty straight forward. Now, for this feature and that - maybe not - but do we need all those features. Programmers like to write code, so every framework always gets bigger and more complicated.I understand and respect the problem react is trying to solve, I just don't see it as much different than the js world before async/await and fetch, etc.. callback hell - which I saw as much more prickly issues.
Mithril is reactive out of the box, and just works. It smokes React in every category, but still has an optional JSX integration if that's what you're comfortable with.
But with functional components I think React gives you something substantially more debuggable than an object instance with random method calls that manipulate the DOM.
React functional components (with hooks) mandate a very specific control flow that probably halves the debugging surface for any given bug, saving time.
You do have to exercise restraint when incorporating third party libraries, but if you can do that it can be a very rapid development environment.
1 https://paulfrazee.medium.com/building-on-budget-4b91b43d0357This is exactly what I do on SPA front ends for many of my customers. Of course we use some libs but those have nothing in common with the frameworks, just narrowly scoped solutions. Saves gobbles of time and money.
Custom elements are really neat, and may very well do what you need. The big pain point that React solves (for me at least) is that you get to define your views declaratively instead of imperatively.
The issue is usually frontend pedagogy or the lack of it. Maybe things have changed but when I graduated undergrad CS in 2017 the extent of frontend being taught in my school by professors was "hand write some HTML, maybe some PHP if you're lucky". I've never met anyone who learned frontend anything in school to a degree that matters, so everyone is either learning from other junior devs on the job, from blog posts that are often wrong, or from a backend engineer who was pressed into learning frontend.
Yes, JS has a very tortured history coming from the fact that it was not intended to be the long-term development language for web interactivity from the start, but we also don't really teach how to do it properly.
I work in a code base like this. People's suggestion? Introduce React!
This is reality, and why organizational chaos is so painful for engineers.
I’ve noticed that over time the prevailing culture in software development has drifted more and more towards “thinking small”. This has certainly been encouraged by the shift to web and mobile apps in an always-connected, always-updatable world.
First it was just the code. Short functions, few parameters, shallow nesting. Make everything testable and maintainable!
Then it was the commits. Release early, release often, merge WIP straight into master in your CI/CD system. We need fast feedback and short cycles to avoid unnecessary conflicts!
Then it was the whole process. Break everything down, do one small thing at once, move it across that Kanban board, next sprint please. We need focus and visible progress!
The danger with all of these is the same: they come from good intentions and even have an element of truth behind them, but they can also mean the big picture gets lost. There’s no coherent vision shared by everyone involved. No-one is watching all the extra dependencies that connect the many small parts. Tech debt compounds. Eventually we are forced to acknowledge that there are “challenges” but even then we somehow convince ourselves that those were inevitable, even though they weren’t really there before.
Yes, of course our early-merged WIP hidden behind a feature flag in CI is completely different to the feature branch we used to use. Now we know everything builds before it can be merged, even though nothing is really testing that all the different combinations of WIP actually work together, so that’s a big improvement on before. And since obviously we have a reliable, automated process for backing out any unfinished code that doesn’t make the cut later, we’re also much better off than the old situation where we’d just have sidelined that feature branch and never merged into the next level up in the first place!
In the real world, this leads to exactly the mess described in the parent comment. There’s no coherent vision for anything big that is shared by everyone involved, no considered and consistent structure for the software architecture or the APIs or the data formats. If we rely on emergent properties that evolve organically, we also run into evolutionary dead-ends, and what survives might work but also be messy.
Personally, I don’t really buy the argument that this is inevitable. Five players can make a great jazz band. With 100 players, you probably want an orchestra with a conductor and sheets and the occasional featured soloist. Neither jazz nor orchestration is “better” or “worse” in absolute terms but they are certainly different.
I don't think this is related to losing sight of the big picture, no more than focusing on good clear sentences means you can't write Anna Karenina.
But I do agree that sometimes the big picture is lost, with tasks that have been broken down inappropriately.
I have a suspicion that partly this is driven by open plan offices: tasks have to be broken down, as focus is a luxury.
For all the annoyances of Hooks, they really are a godsend when it comes to composing state. And refs do indeed suck, but they sucked even more with class based components. I can’t tell you how many times I was able to solve a complex DOM state issue with custom hooks wrapped around refs.
Newer tools like Svelte and Solid do look promising as far as removing boilerplate, but they personally feel a step too far off the path of “it’s just JavaScript.”
Has anyone else here successfully left for greener JS pastures?
I have never understood why people find state management challenging. I suppose its because they are stuck in a framework or MVC mindset where everything is a separate component and each must have separate state models that must be referenced frequently and independently. I just put all my state data in one object, save it on each user interaction, and use it only on page refresh. I have an entire OS GUI working like this and the state management part is only a few dozen lines of code.
Frameworks are like being locked in an insane asylum. You may not be crazy, but everybody else is and you have to follow all these extra rules you would never follow with freedom in the real world. But, the insane asylum provides you meals and bedding and many people can't be bothered to figure that stuff out without necessary hand holding.
I've given it a fair shake and I wish I could change this opinion. I'm trying not to rant so I'll stop here.
I do a cathartic exercise every time this stuff gets to me at a themed twitter account. It's pure vitriol: https://twitter.com/sisnode
[1] oftentimes I see the anti-react argument as "react sucks which is why I use Y framework". I'm saying they are all(?) a toxic trend that leads to terrible products; more complicated, harder to maintain, aggressively encouraging a glossary of programming antipatterns passing it off as elegance.
The more you deviate from one of the set of common conventions, often enforced by frameworks, the more you have to build on your own.
The larger your project gets, and the more developers who need to understand your code, and the more developers you need to hire and get onboarded quickly, the more that those extra rules start to make sense.
And once you've learned and internalized the patterns necessary to build larger systems, it becomes force of habit to just use it wherever, even if it's nominally easier without them. For most developers, a go-to pattern will be faster than working from first principles on every project.
Then you exactly describe MVC...
> state data in one object,
This is your model.
> save it on each user interaction,
This is your controller.
> and use it only on page refresh.
This is your view.
MVC is a relatively simple concept, and it works well.
We must work on very different kinds of projects. I really can't imagine plain JS being a viable or appealing solution compared to Vue, which gives me very little grief at all.
Doesn't that mean you can never reflect state updates in the DOM without refreshing the whole page?
> I have never understood why people find state management challenging
Automatically determining what DOM objects need to be updated in response to a change in state and updating them without a page refresh adds a lot of complexity, if you completely ignore that requirement then state management would be simple like you said.
I haven’t tried Solid, but Svelte has already paid considerable dividends for our team. Frontend feature development pace is faster, the code is generally leaner, and the developers love working with it.
Is that just down to Svelte, though? Or is it the greenfield effect? Projects always fly quickly at first, when the code is fresh and unconstrained, and you're building the basics.
My concern would be when things get complicated and you have to debug Svelte's binding magic - and I have very bad memories doing that in Angular and Knockout.
Also, it has amazing ESLint rules that really help keep all your code aligned and following best practices.
This is so silly. Hooks are literally hooks into a scheduling algorithm you are at the mercy of. That scheduling algorithm is pure JS. Of course you don't have classical control flow in someone else's scheduler.
The key users are referring to when talking about "JS vs magic" is that React is just constructing objects and invoking functions, with known semantics, whereas Solid and Svelte are auto-magic-reactive systems without clear semantics rising from the choice to do even more things in their internal black boxes than React, such as never re-rendering components, dark compiler magic on syntax that doesn't naturally map to a JS tree, and other such things
They literally are.
> hooks aren’t JS
The code in React executing components relies on hooks running in a consistent order each time the component runs, but the existence of a runtime context that has expectations about side effects (and hooks are side effects) doesn't make it “not javascript”.
Lit is the anti-framework because it isn't a framework. Once you remove all the cruft, you can spend a lot more time writing the code that you want to write and, doing so in the way that you want to do it. Everything discussed in this article is solved by bare metal web components.
It is built on top of web-standard; I am surprised how (lit)tle attention it gets on HN (so far).
That is why it is not discussed that much. If you need to write components, then lit may work for you, but whoever wants to ise those components would prefer a js framework.
Also, lit is from Google. which makes it worse as Angular is from Google as well
> I still think Vue (especially Vue 2) is the greatest of them all. I've worked professionally with Angular, React, and Vue. Each for several years. Vue is easily the winner for me with the syntax that closely matches native HTML and JS/TS and how it encourages clean separation of concerns and clean code. JSX/TSX is absolutely the worst for the latter, it's like the wild west. "But you don't have to write it that way" - yeah ok, but when you work in a large organization it's going to get written that way by your peers; good luck trying to stop it. Angular is just a clusterfuck of pipes and observables that require higher cognitive load to unravel when trying to understand a semi-complex block of code. Contrast with Vue where I can near instantly understand what it's trying to accomplish.
> This shit right here - {#if <condition>} {:else} {/if} - is why Svelte is deterring me. For the love of god, can we stop coming up with weird custom syntax for templating code? This is one area where Angular also pisses me off: *ngIf for example is just as hideous. With Vue: v-if, v-else. You add it as an attribute to native html, it's dead simple. No weird symbols, no braces or other oddball shit I have to look up when I step away from it for a few months and come back. It just makes sense right away.
That being said, the Vue2 -> Vue3 migration has felt pretty frustrating. It's felt like, with enough work, I could eventually get Vue to behave similar to React.
I've simply stopped doing any frontend development contract work *. Until the whole JavaScript world pulls its head out of its bottom, I'll leave that niche to other people.
* I'll do JS and React if I really have to, but I'm actively avoiding any client-side focused projects.
I wish I could write everything with it.
I don’t believe in quitting cold turkey and I don’t think there’s anything available to fully replace all aspects of React. We’re instead gradually transitioning our internal ecosystem to use less React-specific stuff, positioning us to migrate (or not) in the next few years.
- Move from CSS-in-JS to vanilla CSS
- Avoid React-specific libraries, both developing them internally or when selecting third-party deps
- Keep business logic out of React components whenever possible
Etc
(1) https://unpoly.com/ (2) https://lit.dev/
React and friends really do make certain things easier and "nicer". But when you sit down and think about it, those things aren't that bad with vanilla JS. It really makes me wonder what value React et al is adding for the cost in KB and mental overhead.
That’s more or less exactly what everyone said about JSX when it first arrived. Now people don’t really consider it. I suspect we might end up feeling the same way about Svelte’s language additions if they ever become commonplace.
You are dealing with particular values and wiring everything meticulously.
Whenever I write stuff using https://rxjs.dev, writing React feels the same.
Using ExtJS years ago felt higher level than React.
Sure, it's often overkill and a functional component does the same thing with less code. Use a functional component in these cases. But if you're doing something more complicated then stop treating class components like the fucking devil. They have their place.
More importantly, I realized React is trying to tame Facebook level of problems and hence their design decision steams from those data points. I develop small/medium type of frontend apps which doesn't need to apply solutions developed for such a scale.
These days I'm using Hotwire[1] (Turbo + Stimulus) with fair amount of vanilla javascript libraries in my apps. Occasionally, when I need to develop a reactive piece of UI I reach out for Svelte[2]
I'm quite happy after making the move. My apps complexity has reduced drastically and there is huge boost in my overall productivity.
Step 2: Someone gets fed up with this writes a framework that "does things right" and is designed for "simplicity"
Step 3: People start loving the new tool because it is so much easier to work with.
Step 4: People start to do things the tool wasn't designed to do. They start making "minor feature enhancements" to the tool so that it can fit more use cases.
Alternative Step 4: Tool becomes "industry standard" so everyone starts using it because it is "the right way", regardless of whether or not it is a good fit.
Step 5: The new tool becomes massive and bloated and overwhelmed with too many features, configurations, and options.
Step 6: Return to step 1.
> The Wheel of Time turns, and Ages come and pass, leaving memories that become legend. Legend fades to myth, and even myth is long forgotten when the Age that gave it birth comes again. In one Age, called the Web 2.0 Age by some, an Age yet to come, an Age long past, a wind rose above the great mountainous island of FANNG. The wind was not the beginning. There are neither beginnings nor endings to the Wheel of Time. But it was a beginning.”
Strong agreement!
There's a saying in community organizing and activist circles that goes something like:
"Don't apply the solutions of the butterfly to the problems of the caterpillar."
Imho it's a really important consideration when thinking about scale of movements (and nonprofits... and any community initiative...). This is especially true when "professional" people are always coming in and confidently over-applying their learnings in corporations to thinking about activism (which often, though not always, has very different incentive structures and lifecycles).
I think about this parable often in regards how all these scaled tech companies end up stewarding the developer tools and therefore practices for everyone. And they tend to out-pace and out-broadcast other wonderful tools that could better serve the majority of developer niches.
In 5 years if no one else is using Hotwire then your code is obsolete, no?
I liked working in next.js in the past but my company was already talking about Nuxt.js when we hadnt even fully converted to next. Feels like jumping ships from a ship with some major flaws, but will continue to float for x+ years, onto a lifeboat that may or may not make it another 500 feet before sinking.
That said, learning how useEffect replaces lifecycle methods is pretty quick to pickup. As is useState.
The lesser known hooks are what are difficult for me to understand. useRef is pretty clear. useMemo? I'm not quite sure what it's for, but I imagine it's not too tough to figure out if I spend a bit of time learning & experimenting with it.
So, although I liked the simplicity of class components, functional ones aren't too bad. They bring in some additional complications, but with a bit of effort it seems to all be learnable to me.
I never used Svelte, but I feel that "develop a reactive piece of UI" is basically Stimulus' job description. Can you explain a scenario/task where Stimulus does so poorly, that you flip the switch and use Svelte instead?
It’s challenging though to keep supporting an API contract that you don’t believe in anymore. Or especially that you are causing you and others pain. I think sometimes maybe the thing to do is pass the baton and work on something else, but we also punish people for abandoning things. Like the author of Groovy, who hopped around from project to project and lost people.
Did you rewrite your entire app?
Class components were/are just fine.
* SHOW HN: UberTinyUltra.js Here's my new super light-weight 4k Javascript Library!
* How I switched to UberTinyUltra.js from PopularFramework.js and simplified my life!
* SuperCoolStartup switches to UberTinyUltra.js
* How I built my unicorn on UberTinyUltra.js
* UberTinyUltra.js 2.0 now with a compiler, classes, typesafety and a C++ like turing complete template language to take on the big enterprise jobs because we have too much time, attracted a lot of developers to the project, and ran out of obvious simple features to implement!
* I hate you UberTinyUltra.js you are too complicated.
* ShowHN: SuperTinyMega.js Here's my new super light-weight 4k Javascript Library!
God it drives me nuts that the language will allow you to spread a class into an object and it will take the properties but not the methods.
That said, the hooks model is far from perfect. They give you a lot of rope to hang yourself with and were badly introduced. Within weeks the internet was ablaze with terrible advice.
When so many people get it wrong, the library is to blame. And I wish hooks were as robust as Solid.js’ signals model.
Vue 3 also has hook now. But none of these defects in the article exist.
In vue.
To use some value in a effect, you just use it and it is tracked.
If you need to clear up your code. You cut some code into a useXxx function, and your reusable utility is done. You can use it everywhere now. You don't really need to specify dependency of whatever by yourself.
If you must manipulate a dom, you just manipulate it. It will work as long as you revert the change before vue want to update it again.(And vue do provide a hook for you to attach a handler when these happen)
These restrictions are added by the way react implements hook, but not hook themselves. Hooks are just more user friendly services (by allow the hook to attach to component instance without all the glue codes(addListener or whatever))
Someone actually did in a project once and that lead to render loops that were hard to debug or solve: https://blog.kronis.dev/everything%20is%20broken/modern-reac...
Instead of a clear message about what caused that loop and maybe a walkthrough of the last N iterations (which changes caused which logic to be triggered), the error message was just a vague and lazy error (even AngularJS errors were better back in the day, filling in details in the docs as URL parameters).
Then again, over the years I've formed the opinion that both approaches are bad, just in different ways. There is no silver bullet, just look at how even desktop software and UI solutions struggled to be workable throughout the decades, of course things won't be that much better in regards to front end.
However, Vue's approach to hooks seems refreshing: creating and nesting components still isn't as easy as in React, but getting things done feels a little bit less cryptic and not as confusing, especially with something like Pinia!
I also think the fear of verbosity in this post is why I can't take it too seriously. Solo maintaining a not-so-old Expo application which uses React Navigation, I constantly have to rewrite large sections of code due to constant refactorings to match whatever coding style is hot at the moment, as the 6 month deprecation deadline is constantly at my doorstep because I have bigger fish on my plate. SO answers being out of date isn't a problem because React is too old, it's a problem because of everyone wanting it to be shiny and new again, and React acquiescing to those demands.
The author does have a bit of a point with libraries, but I counter with Nessim Btesh's fantastic article regarding proper React style[^1]. In short, you shouldn't really be depending on libraries for production needs, just for prototyping.
[^1]: https://nesbtesh.medium.com/how-i-write-react-after-8-years-...
How so? There is nothing that a class-based component can do that a hook-based component can't (EDIT: Except for error boundaries). I'd go as far as to say class-based components are strictly inferior because they force you split your behavior logic across lifecycle methods and are not easily composable. I've been able to support much more complex behavior easily with hooks (e.g., connection management), that would have been a nightmare with class-based components.
That's not strictly true. You can implement "hooks" on top of a class based components. It's essentially the strategy pattern.
Great article, couldn't agree more with them.
Yes, seriously. Have you used it for more complex components? You can split your effects across multiple useEffects, and have a guarantee that they run completely independently of each other (especially since you know what their dependencies are). Compare that to lifecycle methods: you only have one per component. All your effects concerned with setup have to be piled into the same function, and their associated teardowns have to be elsewhere in another method. How do you audit a behavior's logic? How do you reuse that behavior in another component? It's not easy, and you can cause weird bugs depending on the order in which you run them.
Compare that to useEffect. Setup and teardown in the same function, which allows it to focus on just one behavior. And if you need to, it can be trivially pulled out into its own hook and reused across all your components.
A lot of third party hooks are really good though, being able to hook a dependency rather than create higher order components saves a lot of time and is conceptually much easier.
Sure methods like componentDidMount are a mouthful but I found it much more explicit
Changing the core methodology of a project used by millions of developers at the time was extremely irresponsible. They basically made obsolete all React educational resources overnight. I'm sure people making money by producing React educational content were very happy about that though.
Hooks just match the way the React runtime works. Classes are just way easier to do weird bad stuff with. I’m not sure I would ever choose React if it was still class based. I’d use Ember probably which gives you more with the class-oriented API. Without the simplified control flow of hooks, I don’t see what React even offers over Ember or Vue.
React is built on components which are objects (both in a programming and GUI sense). Using OOP to describe them just makes so much more sense to me.
The arguments I’ve heard is that it is “cleaner”, “more understandable” or the OO style is preferred. Some developers feel uncomfortable with hook magic because it introduces a more niche programming concept than OO (e.g stateful functions). I don’t find these arguments compelling. What the React team managed to do was reproduce the same functionality as class components while decreasing the footguns associated with them.
1. https://reactjs.org/blog/2016/07/13/mixins-considered-harmfu...
I disliked hooks after first trying them on their release, and I hate them now.
I'm still bitter that a senior engineer didn't step in and prevent them from destroying the react ecosystem. I wonder if they were have been squashed if React was developed outside Facebook?
Has anyone branched that into its own thing yet?
> Form libraries are portly maintained or documented
The two-way data binding in Svelte saves ~4 lines of reusable hook function but doesn’t come close to covering defaults, validation, errors, dependent fields etc - all of that is essential complexity.
Write your own form field hook. The state model for a form is simple - what is a 3rd party library bringing to the table in terms of state management? I have more pain explaining what I want to a 3rd party library than I do writing a bit of code myself to do the same thing. I reach for open-source input components from a vendor for things like credit cards, but manage the state myself.
> useContext and useEffect have annoying dependency tracking; I prefer Solid’s mutable state and automatic dependency tracking.
React’s built-in hooks are positioned as building blocks with clear-cut contracts. Why dump Redux for bare useContext if you prefer Redux’s selectors? Just use Redux. If you like the Solid style of automatic dependency tracking and mutable state, just use Mobx - it’s a mature library for that style that’s been around for 7+ years, and is used in production by products like Linear and Cron.
Overall I agree that function components in React are difficult to get right for developers and a bit too verbose. There’s an onboarding tradeoff to every abstraction you layer over the framework’s APIs, but there’s large advantages too. Do so judiciously to find the sweet spot for your team & scale.
Based on the RealWorld projects, Svelte saves you around 50% in loc vs React.
https://medium.com/dailyjs/a-realworld-comparison-of-front-e...
Sure. but it is better for 99% tasks. And remaining 1% can be achieved via other APIs
The part about the Inspector component and the rules of hooks I don't understand. I would have put the visibility state outside the Inspector component, and then there is no need for any conditional calling of hooks.
I know and agree this has been discussed to death, so please be patient while I beat this long-dead horse.
In the beta React docs, Context is described as: "Context lets the parent component make some information available to any component in the tree below it—no matter how deep—without passing it explicitly through props." [1]
In the docs prior to that, they go over how to use the useState hook to manage state [2]. In the paragraphs immediatly following, they describe how to drill props to pass that state around to child components and how to lift it up so it is easier to share [3].
So, while Context may not explicitly be about state management, it's positioned as a way to make managing state via prop drilling much easier. It's easy to see how someone new to React would come to the conclusion that Context is about state management. The React beta docs all but say so.
If the React team doesn't want developers to think about Context as state management, they should not implicitly position it as state management.
[1] - https://beta.reactjs.org/learn/passing-data-deeply-with-cont...
[2] - https://beta.reactjs.org/learn#updating-the-screen
[3] - https://beta.reactjs.org/learn#sharing-data-between-componen...
To me one of the biggest issue is that almost everyone is deeply afraid of prop drilling. Passing props around and having some local state works very well for large parts of many applications. With hooks we don't have deeply nested Higher-Order components anymore, if you pay a bit of attention you can have reasonably shallow component hierarchies and props work very nicely then.
Server-side state is a special case, I find that much easier to handle with something like React Query. But once that is done I don't see much need for any external state management library like Redux, useState/useReducer works pretty well.
And yes, sometimes Context is useful for state that doesn't change much. But those are the cases where the drawbacks don't matter. But that is a special case, not necessary for the vast majority of state.
Honestly, this is a point of confusion I see folks ask about _every_ day.
Context is a Dependency Injection tool for a single value, used to avoid prop drilling.
Redux is a tool for predictable global state management, with the state stored outside React.
Note that Context itself isn't the "store", or "managing" anything - it's just a conduit for whatever state you are managing, or whatever other value you're passing through it (event emitter, etc).
I ended up writing an extensive article specifically to answer this frequently asked question, including details about what the differences are between Context and Redux, and when to consider using either of them:
- https://blog.isquaredsoftware.com/2021/01/context-redux-diff...
I think the conflation came more from the "community" than from the React team itself. Many people were (ab)using Redux for simple cases were Context would have been sufficient and, in the process, paying the price of having a lot of boilerplate related to writing anemic "reducers" whose only purpose was to store things on the global state, and eventually also paying the price of having a ball-of-mud of a global state where no-one could be sure of the dependencies between components, as anyone could be accessing anything on that big ball-of-mud. (Been there; not proud.)
So, when the Context API came along, with a much leaner API (no actions or reducers) and less "global-y" feel than Redux, people who were misusing the latter naturally saw the former as a good replacement
One of the biggest level ups I had was extracting complex hooks into their own .js files instead of stuffing next to component code. This helps a lot with readability/reusability.
Overall I liked what I came up with, but it felt like a lot of inventing stuff on my own, which I'm not sure if the next developer who comes along will appreciate.
If one member doesn't understand all of the nuances of useEffect or paint useCallback, they can write a component or custom hook that another team uses and gets subtle bugs from.
For example, I need to look inside of a hook to see whether the callback it provided was wrapped in useCallback. That means I can't truly rely on the abstraction since I need to learn it's internals.
What's funny to me about this is that React people expressed so much hatred for Angular 1 for its "digest loop" and the difficulty of debugging infinite digest loops. And here we are, difficult to debug infinite digest loops in React that don't even give you the courtesy of raising an error after too many iterations.
React is amazing. And what I see is that people find so many ways to shoot themselves in the foot. At the same time, I understand that batteries-not-included approach will lead to that result.
First of all, people get out of their skin and try to make it a complicated and entangled mess. In programming, there was always a semi golden rule, to not architecturally fuse your business logic to any particular framework. We logically isolate our app from whatever framework we are using at the moment.
I’ve given the same demo day (task) exercise to ~50+ react devs. Basically two inputs and a button to draw a pattern on an HTML canvas. You wouldn’t believe how many of them (roughly 47) completely and unnecessary implemented most of the business logic inside react components. The sad part is this app doesn’t need react at all. Or could implement 99% of the BL in pure JS and just call one function from React.
React gives an option to store business logic state in `useState` and manage it via `useEffect` and we gladly use that to our demise. Litter our code with singletone instances of a business logic because we’ve “forgot” how to do a DI without a framework. People write `<IF>` components for christ…
Second there is this immutable transducer reselect memoized concurrent proxy suspense state promotion society. Performance zealots obsessing over trillion renders per second. Which they don’t deliver, by the way, beyond todo examples. They describe how their Rube Goldberg flux redux machine works. A machine to read and write data into a JSON object.
In a nutshell, the we should critique ourselves, not the framework. IMO the framework is doing it’s job just fine. Unless we learn what really goes wrong with react projects we are doomed to repeat the same mistake again and again with next (pun intended) framework.
React is the one tech that really freaks me out because every time I have to dive into it, it's a completely different beast and it feels like so many people actually writing in React are just effing CRAZY
Years ago I wrote a 12-line-or-so JS "framework" that did one-way databinding, it accepted a fragment of HTML with template strings and replaced those with the data you wanted... it could handle thousands of updates without falling over (though updating the whole table would freeze the browser for a half second) and last I checked was still in production doing fine. But now nobody understands it and I'm fielding weird questions about why X framework wasn't being used in 2015 when I wrote it. And it feels like 90% of what I do in React is the same thing! (Simplifying, sure, but my point is that React feels bloated as hell and reinvents the wheel way too much)
> feels like so many people actually writing in React are just effing CRAZY
Same here. We had an experienced-but-batshit-crazy dev create a React site with approximately 250,000 LOC to support 3 forms with a max of 4 simple inputs and a 3-column data table view. To this day it makes my head spin how it is even theoretically possible to write that much code for so little functionality - much less _actually do it_. And don't even get me started on how unmaintainable it is - IIRC I tried to update the string content of an error message once and it required changing something like 57 lines in 10 files (or maybe it was 1 line in 57 different files? something like that...).
I would love to point to a good repo, but all the apps I’ve worked with are closed source. And I haven't seen a good open one :/
Pinky promise to finish a response here or write a blog post and post a link here. But have to go now to get some sleep
The more power we give to developers - by simplifying, abstracting, optimising - the more they want to achieve, the faster they want to do it, the less effort they want to spend.
Which puts them right back in the position of working under complexity.
The reason people are now complaining about React is that React made writing 2015 webapps extremely easy. The result was our appetite and ambition stretched to 2022 webapps and we blame the framework for making things harder, not ourselves.
And then people start adding unnecessary useState or useEffect to their demise.
It's hard to blame them honestly. When React is one of the first thing you learn, you don't know the other possibilities. Many probably haven't used any framework outside it. And most probably don't know what problems React help solve.
And when they start developing, they solve problems by using what they know: The React tutorial that taught them to use useState and useEffect for every state and logic.
SolidJS is following the same principle like Vue + Composition API. I think Svelte is also in the same boat. This kind of pattern is so much easier to use and reason about. React Hooks become really painful and annoying really fast.
React is doing innovative, groundbreaking stuff with React 18 and beyond. I've done several talks on them and will use React when I have those needs. but other folks (I went with svelte in 2019, but i'll also acknowledge qwik, solid, and vuejs) are doing cool stuff too, and most people are usually not building facebook. Ecosystem for React is strong but size isn't everything; once you identify the 10-20 things everyone shouldn't DIY you're basically done (I ended on this at last week's Svelte Summit https://www.youtube.com/watch?v=A8jkJTWacow)
And how much logic you put into a component is up to you. Your components can be almost entirely view logic, with any other data fetching or computation offloaded to other regular js files.
One of those rare exceptions, where overlaying another song improves the original: https://www.youtube.com/watch?v=huEtJw7pfLk
By the way, all section titles in this article are song or album titles.
I generally empathize and see what a lot of authors are saying when they have these commentaries. But I just cannot get past the reality that for all its issues and things worth complaining about, React has never really been "in my way" when it comes to the only goal I really care about: shipping a maintainable product on-time.
I think that comparing a 10 year old library to much younger libraries can be useful in some cases, but is still apples to oranges, given that younger library still has to survive growing up and not having the same claimed fate.
JSX yuck.
I've looked at Svelte. I was deterred by lack of ecosystem. Vue's is just big enough to be workable, I think Svelte doesn't offer enough to reach the Vue/React size - probably.
I fail to see the point in SPA apps for the majority of web apps. At my work we have an ancient Dojo frontend and a newer react one being build. Its a few list views that the user can filter and a few forms with validation. It's a ridiculous amount of complexity to avoid loading a page. I could do almost everything for half the effort with server side HTML from Django and a little bit of JQuery.
I ask at my work why we are doing this and no one seems able to give me a decent answer but we carry on down this path regardless.
I work on regular ol' websites, that have some pages containing some moderate interactivity – so a few years ago the project adopted React to handle the frontend. I've found the experience mostly OK, but I sometimes get the urge to simplify things by leaning more on server-rendering / maybe using the URL to capture state...
and then realize I can't, and that this is Just The Way It Is Now. Because now all our devs expect to use React (or more accurately, its vast ecosystem of 3rd party functions of varying quality) to accomplish anything, and we forgot to screen for hires who can write their own JS+HTML or who understand how the response/request process works in a web application. (oops!)
"React hires are easy to hire for" so that often drives decisions, but I am skeptical my project ever needed React (or any hyper-optimized-for-SPAs-feature).
Most everyone using React should be using Nextjs or similar level of frameworks. Server rendering comes out of the box. On the other hand, it really isn't too complex anymore. With Next, Sveltekit, Remix, Nuxt, and friends, you get simple, hardened tooling out of the box. And you've decoupled frontend from api which many teams find very helpful to split responsibilities and allow certain features to move at different paces.
Yes, in React, with Next.js.
I'm going to have to dynamically create html and attach event handlers based on data either way. Much rather do it all in Typescript than remember Django templates DSL and still do javascript, for the result of worse UI's, poor 3p library support, and awful state management between frontend and backend.
https://dev.to/rajasegar/html-over-the-wire-is-the-future-of...
thinking of forms as a model that can be rendered is really valuable IMO. most UXes would be 10x easier to write if we had better standardization around the 'schema to UX' step
'form first UX' would also reduce the work of building cross-platform apps
guessing django is ramping up on its AJAX, but I think a real forms standard would need to understand AJAX -- users aren't going to remember to scroll down to a 'save' button. realtime validation also an AJAX issue, typeahead as well.
See Next, Nuxt, Remix, SvelteKit, Astro, Qwik City, etc.
When you want to have "early return" in a component, it's possible to just split the component into two to introduce an "inner" component that contains whatever comes after the early return point in the original component.
Any local variables from before the early return then need to be passed as props to the new inner component.
However, naming this inner component is always an issue for me. Typically I just name them Inspector2, Inspector3, etc. until I have an implementation that works. Then I can start thinking about how to refactor the implementation into something that I can give sensible names.
It's really a code transformation similar to async/await where you derive a state machine from a block of code by identifying transition points. In async/await you have transitions at the await points; in components you have transitions at the early return points. In async/await, no one is forcing you to name the individual states or even write them out explicitly - the compiler takes care of it. Then why do I have to write out the state machine explicitly and name the states explicitly for function components with early return? I would love for React to have some facility like "Consider the rest of this component as a new sub-component; as if this component now returns a single Element pointing to whatever is in the rest of this component". But it's probably a Hard Problem™.
Wrapping components lets you have layers that don't have to care about what's going on inside. HOCs just let you write those layers in a way that can be composed. In your example, `isVisible` could be its own HOC, and then the inner component can have whatever name it needs.
Also, building a team is much easier. If you bring six React developers around a table, they will have six different ideas about the parts of the ecosystem they like and use.
Six Vue developers? They’re ready to kick ass from day 0.
In time, I believe React can still take us forward.
Frontend craft has also become too complex in general. We really need to step back and decide whether all this fucking shit around SPA, SSR, route splitting, transpiling, everything-as-a-type, "me too" features between frameworks, overemphasis on trendy designs, package managers that wrap around other package managers, REST or GraphQL or RPC, etc. etc. etc. Much of which serves mostly to create corporate careers for developers more than to actually serve users.
React takes us forward in the sense that most of us don't want to go back to direct DOM manipulation or jQuery, and it's simple enough that it doesn't come with the same baggage as Vue, Angular, Ember, and so on. Even if React itself were to fade, the institution that is React I think will continue on for the foreseeable future.
There was recently a demo of what a Todo MVC app might look like if written in vanilla JS with today's apis. It looks fairly decent; I could see myself going back to something like that:
https://frontendmasters.com/blog/vanilla-javascript-todomvc/
I'm a developer that's done with this, but it brings in VC money so in my experience it serves mostly to create opportunities for CEOs to get some more money on the table via buzzword pitches.
I took a risk on React back when there were minimal components and zero form libraries and started fading away from React once hooks were added. Redux is still pretty nice, and overall still use React because I much prefer it to anything else. But we're definitely in the long-term bloat phase which you can tell by the popularity of things like Vue, "React Lite" libraries being built, etc.
More here:
A lot of React code I run into these days would be simpler and less verbose with a traditional (non-function) React component.
They'll almost certainly never go away without a major version bump because it would break way, way too many sites, but at some abstract level people feel class components will have a shorter shelf-life than functional. And certainly, any given third-party integration to React in the future is allowed to just say "We only support functional components" (with the corresponding hit to popularity, of course).
Interesting that you didn't comment at all about my take on functional components not being the best tool for every job in React. Curious – do you have an opinion?
There's very little need for a
onChange(e) => setFormValue(e.target.value)
and all the extra work that comes with that.There is an absurdly simple solution which removes all the 'cruft' the article bemoans: use uncontrolled form elements that then send the form data to the server and have the server do the validation, error messaging etc.
In this approach, the client gets back into the business of pure network calls. When the server sends back a response parse it and update the UI. This is trivial with some conditional rendering of error messages, styles etc.
But rarely is this done. This approach is why I really like using Remix. It encourages this paradigm by coupling the server side code that runs on a form submission in the same file as the client side code.
All this becomes blissfully easy.
If you're tired of React and the npm lunacy, I recommend you take a dip into Elixir and Phoenix Liveview. Take it for a spin, see how much better it is for you and your users. It's _sane_. You don't have to "split the context" anymore. xD
https://pragprog.com/titles/liveview/programming-phoenix-liv...
Phoenix Liveview, Elixir & OTP, and Full-stack Graphql with Phoenix.
Best way to learn Liveview.
"There are a few good use cases for refs:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
Avoid using refs for anything that can be done declaratively.
...Don’t Overuse Refs:
Your first inclination may be to use refs to “make things happen” in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to “own” that state is at a higher level in the hierarchy. See the Lifting State Up guide for examples of this."
If you are using refs to talk to your child inputs you've made a mistake. React talks about RAISING THE STATE - for complex forms you need to make all of your inputs controlled, you need to manage the state at the highest level. You need to push those changes down to child components, none of them should have their own state.React gives you some nice tools to handle most common use cases but you can still make less than optimal decisions.
The best way we found is a custom useForm hook (simple to code, using useState's):
const { submit, getProps, isLoading, error } = useForm({ onSubmit: cb, initialValue: {} /*optional*/ });
return (
<form submit={submit}>
<input {...getProps('firstName')} />
very flexible, you can use other wrappers than form or inputreact-query is another super useful lib, you could use useMutation() for implementing useForm actually
And for now, this reason is good enough for us.
Just hire React devs and give them a couple of days with Vue, no problem
In general Vue holds high conceptual similarity to React so React devs just "get it" straight away. However nearly everything is simpler / easier than it would be in React, so they learn it extremely fast.
A pre-compile step that statically adds dependencies to your code might be a reasonable compromise, but those dependencies do need to be explicit.
The rest of the stuff makes a lot of sense though.
What are peoples thoughts on Svelte vs Solid? I know Lit/native web components are out there as an option as well. I have past experiences with it when it was Polymer.
If anyone is looking for something very lightweight they may want to consider Alpine as well.
So annoyingly enough... There's not anything inherent to the language that makes it impossible to get a list of closure references for a function, other than it's not something JavaScript exposes. In a hypothetical future version of JS, this could be a first-class feature of the function API.
... because what JavaScript really needs is yet another feature to support one framework's special-caee needs. ;)
- Bjarne Stroustroup
I'm gonna say something crazy here, but hang with me: hypermedia.
an enhanced hypermedia model offers a lot more interactivity often at a fraction of the complexity, and w/ the benefits of the REST-ful architecture (flexibility, etc.)
What is currently the best way to handle the complexity once you have a ton of asyncronously-loaded JSX components which have complex conditional rendering logic based on multiple inputs and datatypes? (including defaults, local variables, and all kinds of other stuff)
At this point I'm inclined to just handle all the data processing logic and when-will-it-render logic in some intermediary controller and then keep the visual templating barebones and strictly separated, though it feels quite un-React-like to do this, and almost more trouble (and certainly more boilerplate) pushing vars through the stack and separating Controller / View than to just let my JSX be messy with useEffect hooks. I suppose this is when I should use a React class component? Or a subcomponent? Or a custom hook..? Or maybe I should just modify (global) state - even grosser from data duplication perspective, but at least then all the data processing is in one clearly defined format instead of scattered throughout templating files.
Last I checked (and burned out) this was the major React hurdle, and the downside of React's simple-by-default templating. This complex stuff got real ugly real fast. What's the elegant solution here, if any? Any improvement?
React's concurrency features should have been broken off into a separate, opt-in library, not packaged into React core, and they sealed their own coffin with this release. That said, suspense is awesome once it's fully understood. Give the public a few more years to get it, though.
5yrs in progress, it’s still not documented let alone fixed. I tried to look at the code, and they make giant PRs and are experimenting with priority queues and bitmasks, which seemed pretty off in the weeds to me.
Take React hooks at its purest for example. I can't imagine the WTF feeling in a newcomer's mind, when the useState(<default-value>) helper function returns the "reactive" variable and a function you have to use to update it (!) do you realize how tricky you end up with your technical DOM optimizations?
Same with useEffect(), where you probably will have your whole function defined right into the first argument, and a list in the second one, very lost in your IDE. Who would expect that an empty array of dependencies would trigger it once? Why we should track all those dependencies at first place?
When recommending a frontend framework, I found Vue to be more dev friendly. However, it's currently in a long migration crisis, because documentation shows the 2 different APIs you can choose. I know that the differences are well explained (although they don't even say that's to be retrocompatible with Vue2 mindset), but for a newcomer's mind, it's like asking: which pill do you want to take? red or green? Choose your own adventure!
Most of these libraries are fine for simple to medium complexity forms. However once you cross a certain level of complexity these libraries become impediments rather than tools. I probably could have used a redux implementation but build my own state management API with a mixture of object oriented as well as functional programming. The values of the inputs persist so you want objects, but hiding/validating/calculating need to be reset for each change so I used a functional approach for these other features.
React is not perfect and its easy to paint yourself into a corner. However as someone who used to write js for IE6 and has forgotten more js libraries that you can imagine (Dojo, ExtJS, Backbone, Ember, etc) I can tell you right off the bat its the best tool for the job. Not perfect, just like Javascript. Just know and accept its limitations and you should be able to get React to do what you want.
```
const Form = () => {
const [formData, setFormData] = useState({ firstName: '', lastName: '' });
function handleChange(event) {
const { target } = event;
const { name, value } = target; // Destructuring in steps to make it easier to figure out where values come from.
setFormData({
...formData,
[name]: value
});
}
return (
<>
<input name="firstName" value={formData.firstName} onChange={handleChange} />
<input name="lastName" value={formData.lastName} onChange={handleChange} />
</>
)
}```
<input {...getProps('firstName')} />
and make a custom hook returning that getProps functionI think frontend technology took things too far.
Imagine the possibilities of using HTMX and any server side technologies like Elixir, Go, or Rust.
I’m sure there are some good points being made out there but those points Are getting lost in the noise which is a real shame.
React kinda ate the web world well, so it's not a terrible bet that it'll keep it going for 3 years...
If you like data layer / DB stuff (and if you're strictly a frontend dev) my suggestion is to just get comfortable with SQL / Postgres / SQLite, and just dbs in general, as that stuff has remained pretty darn stable for a while. Only really replaceable by even-simpler (though less versatile) approaches like giant plain key-value stores, file-system-style storage (NoSQL), or added versatility to Excel spreadsheets (lol it may be more viable to let companies just keep using those than moving to a DB nowadays just to let office workers use what's comfortable). I learned SQL 15 years ago and still glad to have it now surprisingly - though it's maybe sometimes more powerful than I need. But good value there.
For frontend: JS, React, npm, docker containers, maybe Vue?... that's about as deep granularity as I'd go for predicting what'll keep another 3 years. Libraries, state management, all the tooling, etc etc - you'll probably relearn more than 3 times over during that... Welcome to (a hopefully well-paid) hell.
All these libraries are built on JS, understanding JS is the safest bet you can make.
If you need CRUD without adopting some "low code" platform and its half dozen containers full of weird databases, react-admin is a fine choice.
I have wondered what svelte-admin would look like.
The difference is that Microsoft languages may have better ppl working on developer documentation, who knows...
What distinguishes your framework?
Who should use it?
What does it do? And more importantly, what doesn't it do?
And the biggest problem I have with hooks is that they make functional components non-functional.
This is starting to look like a pattern… :)
1. yes, un/controlled forms are messed up and need work
2. yes, contexts should replace Redux and Redux should go away. but in practice having a few contexts is pretty manageable. Currently we use Redux for all the dynamic state and contexts for everything else and it works pretty well. It is quite easy, also, to write your only little `useSelector()` wrapper around a non-mutating context value (ie: the context has a callback on changes, you listen and filter updates, then update a `useState` to trigger rerender)... but but maybe this a bit too abstract to actually want to do.
3. yes, forwardRef is silly.
4. yes, useEffect is kinda broken. My main issue which wasn't discussed much here is that the difference between `useEffect(..., [])` and `useEffect(...)` is insane. The latter should not exist, or have a different name entirely.
Otherwise, I really don't mind tracking deps _that much_, although I hate that it's hard to have an effect that updates 'on some local variable changes but not others', so in practice I pretty much ignore the lint warnings all the time.
It all feels like "there's a DSL waiting to be written for this stuff, similar to how JSX makes createElement() more ergonomic, but it doesn't exist yet".
That said, Solid doesn't seem like the answer. Solid's even worse: now you're using JSX for if/else statements, instead of the actual DSL you want that doesn't exist yet. Good luck running a debugger on that. (Although actually, if the Solid AST ends up in the Devtools view, that's pretty good. But still: this needs a fully-featured DSL).
5. +1, yeah, the continuing addition of weird hooks feels like a mistake that will eventually be regretted.
6. The right way to do your Inspector example is to have a parent component that decides whether it's rendered, and a separate child which does all the event listeners. It is a bit weird to me that React doesn't allow this to be done in one place. The closest thing is defining another component inline in the first one and then immediately rendering it. But for readability it's best to just have two separate components.
7. +1 to old docs getting in the way. I'd love to see a React 2 with a new name that deletes all the class component stuff entirely so that all of the docs about it are up-to-date. I've been toying with trying to work on this myself.
8. Yeah, fuck FB
9. Yeah, I'm not going to any of the other frameworks either. React is great, it's just ... not... great enough. I am in the middle of writing a series of blog posts about exactly this and I haven't finished yet but anyway here's the first one that I have written about why I like it so much? https://alexkritchevsky.com/2022/09/17/react.html
> We've been together for almost 10 years. We've come a long way. But things are getting out of hand. We need to talk.
> It's embarrassing, I know. Nobody wants to have that conversation. So instead, I'll say it in songs.
Am I the only that finds this open letter format extremely cringey? I almost didn't want to read the article after that intro. The content was excellent otherwise.
I spent years working on Angular before changing team, and there everything makes sense instantly. It's just normal programming, normal MVC, normal everything, and you basically only need to learn about the syntaxic sugar for data-binding and stuff like this.
In React it seems like they reinvented the wheel and they thought a circle was too easy so they made it in a 5 dimensional shape. You have 18 ways of doing anything and they are all deprecated, your logic and your template are mixed which turns your application into an unreadable mess, every single aspect is overengineered to death, requiring you to call useState() to have a fucking boolean that you can update, everything is stored in a variable or declared as a function instead of in classes, etc.
I'll have to give a try to React from scratch where it's not on an existing codebase, but I don't think that's the real issue.
It's been more confusing for me to go from Angular to React than to learn a functional language after years working with imperative ones.
A lot of people really hated it but it's opinions and structure helped promote a more maintainable codebase. I don't miss tuning watchers and the digest cycle though.
All the React projects I've inherited are nightmares. I'm comfortable working on them but they are harder for junior and mid-levels to hop into.
The number of ways to do things in React is a benefit and a curse.
We had so much prop drilling early on. The idea that components should only have what they need is nice, but you need some data to flow to children. And when the UI needs to change on a prop drilled codebase, holy shit, refactoring is a major PITA.
Luckily we have some alternatives to avoid the prop drilling but it's so fragmented.
If something like Mobx was just part of React from the beginning, the ecosystem would be in better shape IMO.
I identify with a lot of the issues the author stated.
React is just at that place where you need a TON more discipline to produce quality code in a team environment.
And because of that, there will be a mountain of shitty codebases in React to inherit, cleanup and maintain over the next 5 years.
That being said, I'm still a big fan of React and Vue though.