So JSX is pure Javascript and not, say, a dialect of XML embedded in JS? Because it sure looks like the former even though it compiles to the latter.
React isn't Javascript. It's a franken-language that looks superficially like a mix of JavaScript and XML whilst following the rules of neither. That's why there is such a thing as a React compiler - a good sign that you're not writing JS, which doesn't have compilers.
The other hint should be all the new rules you have to follow. React is full of magic syntax that looks like functions, but e.g. you can't call them inside an if statement, or you have to redundantly declare the 'dependencies' (variables you read) and so on. The semantics of React code are very different to imperative straight line code.
> So JSX is pure Javascript and not, say, a dialect of XML embedded in JS?
It would be best to think of it as syntax sugar for create Element() function calls. You enter JSX with angle tags and insert expressions in braces
> React is full of magic syntax that looks like functions, but e.g. you can't call them inside an if statement, or you have to redundantly declare the 'dependencies' (variables you read) and so on
That's not magic syntax. That's just functions that must be processed in regular order between render ticks.
It's not a difficult exercise to write a "plain JS" function that works that way. If you've worked much with closures you can visualise how that would play out.
Hooks are magic syntax without any doubt. All magic syntax is made up of non-magic parts, that's kinda the point.
The way you know it's magic is it shatters the principle of referential identity, which tells you that a variable is itself. It pretends you can use a simpler mental model but you really cannot and must not.
Hooks aren't magic syntax. The problem with hooks has nothing to do with syntax. The problem is that the React crowd has decided to overload the meaning of a term that has had a reasonably solid interpretation (at least in comparison to the React crowd).
"React Functional Components" have nothing to do with conventional functional programming. They inherently (intentionally?) violate the spirit of functional programming through hidden global variables, while pretending that it is still functional, when in reality they should be doing the opposite. They should be upfront about the hack that hooks are. They are not functional, they are merely functions. In all other respects they operate as if the function was a method inside a class and that all hook calls are with respect to an invisible "this" keyword that contains the component's context. Since hooks are not named (which would expose their inherently pseudo-OOP nature), they associate their state through a hidden incrementing counter. It's like you're sweeping all the OOP parts under the rug to pretend to be functional so you can be a cool functional hippie too.
Aren't hooks just composable effects implemented in userland? In fact, Sebastian actually asked the ECMAScript committee for effects a la OCaml 5.0 effects (https://ocaml.org/manual/5.0/effects.html) and React only built their own when the committee said "not right now", if I remember correctly.
The thing is ... you can model effects with the Free monad (or with Haskell's Backpack as was on here just the other day - https://news.ycombinator.com/item?id=45221112), so they are definitely "functional", for most variations on the definition of "functional" that I know.
Yes, that's exactly it. React is presented as functional but it's still just stateful components, except instead of OOP syntax and features making that clear, it's hidden away so that it looks functional without actually being so.
This happens because GUIs are inherently imperative constructs.
What makes it even worse is that in order to match the hidden state with the output of a render function, it has to be "reconciled" which uses usually-right heuristics to match the tree structure of the function output to the tree structure in the fiber cache.
Yeah, well put tech wise (the ad personam part puts me off a bit, though). Personally I like hooks quite a lot, but e.g. I feel I have to check once a month or two if a memoized component (that’s „functional” in name) re-renders if its props stay the same and the only change is the state..
Yeah, that's vanilla syntax. The semantics are fairly magic though. The component function that calls useState though isn't a normal function, it's a function that must be called in a special way by the React runtime in order to line up all of the hidden state that React maintains so that it can magically infer the data that your `useState` call maps to and then there's more magic to maintain the lifetime of that data.
Yes, but it is not syntax. It's a contract with the library. React is completely usable using vanilla JS syntax. Same cannot be said for Vue and Angular.
It feels a bit like talking about apples and oranges in this thread.
Your example usage only implies what I would consider the non-magic implementation behavior. I could fulfill that contract with `(initial) => { let s = initial; return [ s, v => s = v]; }`. No hidden magic there, and no chance of breaking referential identity.
> It would be best to think of it as syntax sugar for create Element() function calls
Thats what makes it a new language. C is just sugar on top of assembly.
Its so strange that jsx needs a buildstep, but misses ton of obvious ergonomics that one could have added. Why className
instead of class? With a buildstep, it would surely be possible to determine if class was a keyword or not based on context
> Thats what makes it a new language. C is just sugar on top of assembly.
I think that's a bad example. C isn't a sugar for assembly: For example what assembly code does a function prototype desugar into? Sugars have 1:1 mappings with host code
Maybe you're just being rhetorical about the spectrum of macros, sugars, and languages, though.
(In my opinion, a better target for that criticism, in JS land, is the Jest test framework, because of all the reordering magic it does that break fundamental ES semantics)
> Why className instead of class?
This hasn't been a constraint since... I want to say React 18, six or seven years ago? I might be misremembering the history but I think that was only a problem in the Early Days when there was paranoia about whether JSX needed to be conservative with reserved keywords
Syntax sugar is language syntax that improves a common pattern from the language (making it sweater). jsx, async/await - things that just abstract common patterns.
> That's why there is such a thing as a React compiler - a good sign that you're not writing JS, which doesn't have compilers.
You say that like it's a bad thing - but it didn't stop Babel or TypeScript from being popular, both of which need a compiler. And being honest, I don't like extra tools in my chain, which is probably why I don't use React proper, but that proves you don't need anything React specific for anything other than optimisation
The only syntax you really want for React is JSX. Which is just because writing React.createElement("div" ...) every time is unergomomic. JSX isn't specific to React. It could easily be a language feature, in fact it is an OOTB language feature of TypeScript.
> React is full of magic syntax that looks like functions, but e.g. you can't call them inside an if statement
They look like function calls, because they are. They're not "magic syntax", and in fact, those rules are explicitly because theres no way around that without implementing special syntax
JSX isn't and hasn't even been necessary to use React. The first version of my company's site didn't have a build step and called createElement manually.
And in the decade I've been writing for React, I've never used the React compiler. But saying that it's proof that it's not JavaScript it's like saying that V8 is proof that JavaScript is too complicated because otherwise it would be fast enough not to need a JIT.
And all those rules? You don't have to follow them either. Use class components. They're still there, and they work fine.
If you also have experience using JSX in a React app, I'd love to hear any opinions/experiences you have with doing the JSX style vs createElement() style coding.
I understand how they map to each other but I've only done JSX. That said, I'm old so calling functions all over the place is in my wheelhouse, and I'd love to hear from folks who have done both.
It's verbose, but it's not all that bad for small applications. Especially when you don't want a build step or tooling. In today's day and age, you can have your favorite LLM do the verbose parts for you if you really want.
> So JSX is pure Javascript and not, say, a dialect of XML embedded in JS?
This is actually the selling point to me. As someone who started learning pure HTML, JSX just makes a lot of sense to me intuitively. It feels like intelligent HTML
JSX is just a syntax extension to JS, and it's not even required to create a React app.
React "compiler" is in fact a transpiler, which is a very common thing in JS.
>React is full of magic syntax that looks like functions
Full of magic syntax meaning 5 straightforward hooks? Surely they are not true free-form JavaScript, but at least the syntax itself is not turned into a DSL
Nitpick: React compiler is not a transpiler. JSX needs to be transpiled, and that's usually done by TS compiler. React compiler is another optional thing on top that's relatively very recent.
All compilers translate one language to another language. Historically compilers targeted lower-abstraction languages than the source language. "Transpiler" is a compiler whose input and output abstraction levels are similar.
The React cinematic universe has a habit of repurposing existing terminology, but they're both transpilers, to the extent that "transpiler" is even a word.
You are absolutely right! (Here I'm roleplaying an AI chat bot which caught red handed hallucinating).
It appears that I was wrong about the definition of transpilation. It's a specific term for a compiler that compiles from a high-level language to another high-level language, even when those languages are the same with no DSL and even when the logic is optimized.
In JS land, transpile was used to distinguish something like elm->JS from ES6->ES3. One was the same language with different versions and the other was different source languages.
Yes, all transpilers are compilers, but not all compilers are transpilers.
`useEffect` is straightforward? Cloudflare recently (like, literally 4 days ago)[1] had a pretty big outage because of the improper use of `useEffect` (surprise, surprise, it was the dependency array), a hook so infamous if you search "When should I use `useEffect`" you'll get approximately 9 trillion hits, with nearly all of them telling you something different, including Dan Abramov himself having to chip in with multiple blog posts telling people to NOT use it because of how much of a dumpster fire of a API it is.
Well, that is really embarrassing for Cloudflare... A recursion in a side-effect via dependencies is a rookie mistake, it's hard to imagine it could slip into production with a proper due process. Maybe they should stop vibe-coding and deploying things to production without any tests or review?
If after nearly a decade swarms of people are still making the exact same mistakes with how they use a specific method exposed by the library, then the problem isn't with the hundreds/thousands of people making the mistake, the design of the method is broken. This type of issue simply does not exist in Vue or Svelte even if people abuse watchers (which I've anecdotally noticed tends to happen from React devs writing Vue code).
Also I just want to point out again that Dan Abramov had to write an insanely long guide to using useEffect [1]. This piece is around 10k words explaining in-depth how useEffect works. That is insane for one of the fundamental, core methods exposed by a library.
You know for thousands of years people are still stepping on rake, the spikey part. And they get hit in their foreheads. The rake is lever by design, but I wouldn't say the problem is the rake.
Disagree about it being a rookie mistake. In the simple case, yes. But consider data used by an effect could travel all the way from root to the max depth of your tree with any component along the way modifying it, potentially introducing unstable references. Maybe it worked when you tested. But later someone introduced a new component anywhere between data source and effect which modified the data before passing it on. That component may have used useMemo. So it looks ok, but it over fires.
You become reliant on all your ancestors doing the right thing, in every situation, forevermore. One mistake and unstable references cascade everywhere. The surface for introducing errors is huge, esp in a large, complex codebase. And we have no way to guarantee a stable reference.
This is why we never modify data but create new data. Data must be immutable. Strictly typed. Always in the form it is expected to be. Otherwise - crash immediatelly.
>But later someone introduced
This is why we write integration tests. Introducing anything without tests is only guesswork.
Sorry I should have been more precise. I don't mean mutation, but taking the data and producing a new (potentially unstable) reference to pass on. Mutating data in react results in under not over firing.
It is practically impossible to have full coverage of all code paths in integration tests, since there is a combinatorial explosion of paths. In a case where you have 3 components each with 3 paths you have 27 unique paths. And no app is that simple in practice. It gets out of hand quickly.
Javascript has warts, React has warts, Svelte has warts, Python has warts... It's easy to shoot yourself in the foot in any tech - it's leaky abstractions all the way down after all.
useEffect usage needs to die, yes. I don't think it's a case against React, given its age.
Otherwise, using React is straightforward. I started coding in it without reading any docs. As someone who used Dojo, prototype, ext.js, jQuery (+UI), Backbone.js, knockout (still has a special place), Mithril, the classic angular, vue (2), svelte, Marko and even Solid (I'm not totally sold on signals), React is the most straightforward by a mile.
Is it the most performant? Hell no. Is it the one with the least gotchas/warts? Nope. Concise? A giant no. Batteries included? It depends on what you're looking for...
But I can just be productive with it, and it makes sense.
Bro lists like 8 different moms whose food he has, then says he likes react-moms food, and you make it sound like that's the only thing he's ever had. Did you even read the comment you replied to?
I can’t recall a single time where I would want to put JSX in an „if” statement.. In fact I can’t recall a single time where I would want to use JSX in a different context than the return value of a render phase.
So from my personal experience the things you mentioned are non-issues in practice. Do you have a different experience?
Ah OK, I see it now—I focused too much on the „magic syntax”.
As for hooks, I guess I mentally treat them as magic that lives in its own lane, and so I've never really thought that I could do anything with them, other than what the manual prescribes.
Everything has compilers now and we definitely want to go that direction. The no build people aren’t close to building any popular consensus, like not close at all. Even plain JS is compile heavy because browsers are just compile targets.
it really depends on how much syntactic sugar you add.
You could go back 13 years, show somebody some JSX and ask them what language they think it is and nobody would be well that's obviously JavaScript with a bit of sugar on top.
React isn't Javascript. It's a franken-language that looks superficially like a mix of JavaScript and XML whilst following the rules of neither. That's why there is such a thing as a React compiler - a good sign that you're not writing JS, which doesn't have compilers.
The other hint should be all the new rules you have to follow. React is full of magic syntax that looks like functions, but e.g. you can't call them inside an if statement, or you have to redundantly declare the 'dependencies' (variables you read) and so on. The semantics of React code are very different to imperative straight line code.