New JSX Transform
reactjs.org
reactjs.org
My initial reaction on this is that this looks like a nice (if minor) DX improvement. Not having to type out `React` when it's seemingly not referenced anywhere makes sense.
The underlying signature change is also interesting. If you need to support IE9, then doing `jsx(name, {children})` is going to save a lot of array slice calls that would otherwise be needed for the `React.createElement(name, attrs, ...children)` signature.
On the plumbing side, there's something that bothers me about this change: given this is seemingly a small life quality improvement, it affects a lot of tooling infrastructure (babel, typescript, etc). From a purist perspective, this has a "cancerous" feel (as opposed to being "encapsulated"). This doesn't mean this is a "bad" change per se, but I do feel like it feeds into the idea that "software these days is way too complex"(tm) that has been popping up recently.
I have the same feeling about much of the React ecosystem (edit: though I'd say "tightly coupled" rather than "cancerous"). The other day some folks were discussing the idea of building a new UI backed by a new GraphQL schema. We started talking about the possibility of delivering the UI independently of the GraphQL schema by using an existing REST API, and the idea was shot down as the GraphQL client we're using on the frontend is highly coupled to the UI components. Changing the data source later would have been a highly intrusive change.
I totally understand the benefits that a GraphQL client provides, but it feels like a serious architectural smell that changing the data layer is such a wide-ranging change in a web app. Wrapping a data-source in an API and being able to change the implementation without the rest of the application being impacted is really compelling, and I'm not convinced it's something worth trading-off.
Now, there are tradeoffs we can make to deliver an end state to a user: we can normalize and incrementally hydrate the data shown in the client and increase visible jank in the process, or we can realign the app's experience and/or data source around each other.
Decoupling UI components from business components is of course valuable, but that's different from taking that a step further and decoupling business components from the data source. The latter is a significantly more challenging proposition.
Also, there's some fuzziness here in what we mean by decouple. REST vs SOAP vs GraphQL as transfer layers can be abstracted away as implementation details, but I'd place a higher emphasis on the shape of the data and how it has to be traversed in the client. If you have a paginated list, for example, and you only want to load it once, but you need to display both a summary and load it incrementally, things can get complicated as more functionality is built out from that starting point, regardless of transfer layer.
Surely something else will eventually displace react and/or graphql, just as previous stacks and paradigms have been overshadowed. But then we go back to my original assertion: refactoring away from the legacy of the tools we no longer like is decidedly going to be more complex if we chose to sacrifice loose coupling in favor of something else (performance, initial dev speed, what have you)
Case in point, esbuild looks like a very promising replacement for some web plumbing. But if there needs to be extra collaboration to support this there, other stacks can take the opportunity to dash ahead (e.g. vite) while the react team is busy collab'ing with all these compiler projects.
There is something incredibly satisfying in rendering an entire UI experience from a single GraphQL payload, without having to build intermediary data structures to stitch it all together from multiple endpoints.
I don't love that the docs tend somewhat to encourage the API option with tighter coupling between UI and data, and the Apollo documentation is in general pretty lightweight by my standards. To criticize on that basis is fair. But it isn't anything like forced, and to write off GraphQL or React as "cancerous" on account of someone having made a less than optimal decision in how to use them seems premature.
Apologies, cancerous isn't how I'd describe it, but the sentiment of the quote did resonate. "Tightly coupled" is more representative of my concern.
Thanks for the counter-example!
Not that you're not well aware of both these points, but I do feel like the point at which to scruple over "software is too complex" comes somewhere well before the point where JSX is happening at all, and I'll admit it's a little confusing to see them apparently in the opposite order here, especially put in such strong terms as these.
After all, there's a distinction to be made between essential and accidental complexity. I'd argue that what both these transforms do is to abstract away a lot of the latter so that the former can be more easily seen and managed, and I think they do a pretty good job of it. (The rest of React, I'm not so sure about, some days...)
I had the same thoughts about React hooks although perhaps I don't correctly understand the motivations. Class components have extremely clear, compartmentalized lifecycle functions, and now the advice is... make everything a function-scoped closure? Let redux do some opaque context sorcery to update your components?
React (and others) hit the paradigm right on the head the first time, IMO. It's change for change at this point, which is fine I suppose.
Not necessarily hoping to change your opinion but maybe this helps:
https://medium.com/@dan_abramov/making-sense-of-react-hooks-...
There's a subtle aspect of this change that may not be immediately apparent. One that, imho, significantly regresses JSX as an independent syntax outside of React (i.e. makes it much more coupled to React than before):
JSX is an SGML/XML-inspired syntax, and SGML/XML are languages with 3 dimensions of element definition: names, attributes and children.
Internally, React elements are represented in 2 dimensions, with children managed as a "magic" attribute, with the reserved magic key "children" (this is for performance reasons I believe). Before now this has always been an implementation detail of React. JSX is flexible enough that other libraries could provide an element factory method with a fully featured 3-dimensional internal representation. This change removes that flexibility.
To clarify the change, currently JSX compiles to:
createElement(name, attributes, ...children);
the new transform is now: _jsx(name, { ...attributes, children });A 3rd-party factory can still break the `children` attribute out into a separate entity in their internal implementation.
All it does is make the interface to the transform messier, by surfacing internal performance-related implementation details of React. This is code smell and I'd call it a syntax regression, but it shouldn't cause any severe limitations on what you can achieve.
Personally I'll still continue to use JSX transforms with my non-React code once this lands. It's a great syntax.
> there are no plans to remove the support for it
But I don't really see what would drive this change if there wasn't any intent to remove it at some point down the line.
Regardless, the wording of the article describes the new syntax as an "upgrade" so it seems likely this will the canonical transform in future.
Having children as a property of the parent isn't an implementation detail of React -- it's just a mapping onto Javascript, where objects have properties, and there's no independent notion of children. The new transform makes things more like Javascript, which seems like a good thing.
Part of the elegance of JSX is that it maps conceptually to the 3 dimensions of it's intended output structure (usually HTML) rather than being restricted by direct mappings to the 2 dimensions represented in JS objects.
--- original below ---
Both transforms work "like javascript"; I'm not sure how the new one is any different in that regard.
While I do think it's a little nicer to not have to import React, they could've done that without changing the factory signature.
Furthermore, I think the value of not needing to import React is slightly overstated. If you're using React & JSX, you'll be importing React in your application at some point regardless (so importing in the JSX component file shouldn't add weight). If you're using JSX without React, you don't currently need to import react anyway (as transpilers provide configuration options for factory methods: e.g. via Babel's react-jsx plugin's `pragma` config key)
That's probably a couple orders magnitude more than a typical web page, let alone one parent node, but maybe that's part of why the change was made?
I also didn't see this reasoning stated in the RFC thread.
but if you meant createElement... i'm not sure the goal is perf? then again there was a lot to cover in that RFC and i may have missed something
W.r.t. the goal being perf., I'm not intimately familiar enough with React's source to know the exact gains (or losses) but there's a fair bit of discussion around perf. in the RFC thread. e.g. https://github.com/reactjs/rfcs/pull/107#issuecomment-520887... re: prop re-use and https://github.com/reactjs/rfcs/pull/107#issuecomment-466487... re: bit flags. There's also a lot of discussion around whether these are compelling, so it might not improve perf. at all.
Secondly, I should point out that JSX is currently not dependent on React. You can use JSX now without React, either via Typescript (built-in support) or via the custom `pragma` config key in babel's JSX plugin.
The change described in the article makes it unnecessary to import the full React lib into each component file when using JSX with React.
I'm not sure what the benefit of this is tbh...
https://babeljs.io/docs/en/babel-plugin-transform-react-jsx#...
> The change described in the article makes it unnecessary to import the full React lib into each component file _when using JSX with React_.
`importSource` is more than just the new `pragma`, it also handles the fancy auto-import magic. This change makes it unnecessary to import your own custom lib (not just React) into each JSX file.
- Deprecate "module pattern" components. (ed: I don't know what this is)
- Deprecate defaultProps on function components. (ed: Presumably, allowing normal param default values instead)
- Deprecate spreading key from objects. (ed: Presumably, performance)
- Deprecate string refs (and remove production mode _owner field). (ed: unclear how this is related)
- Move ref extraction to class render time and forwardRef render time. (ed: presumably to make refs "just work" and not require the forwardRef rigamarole)
- Move defaultProps resolution to class render time. (ed: performance? not sure)
https://github.com/reactjs/rfcs/pull/107
[append]
Much better link: https://github.com/reactjs/rfcs/blob/createlement-rfc/text/0...
And the thing is, even if it were, what a parser/compiler does to represent a language is its own completely independent thing.
At one point I had to use XML in JS (back in the day, haha). I parsed the XML and represented each node as a single object, with `__name` and `__children`. They were "one dimension" in my representation, but also literally a compliant XML parser— more compliant than JSX.
This isn't really true of JSX; I'd suggest giving it a try to see exactly how the system works.
To summarize, there are 3 parts of this discussion:
1. The JSX syntax (part of the JSX spec.)
2. The JSX transform (part of the JSX spec. & what parser/compiler must represent the language as)
3. The template function implemention: 100% independent and can represent nodes any way they want
In your comment you're imagining a system where 2 & 3 are coupled, whereas this isn't how the JSX world works. With JSX, 1 & 2 are coupled, only 3 is independent.
Ah, ok I see where this misunderstanding stems from. See, this is not part of the spec. With JSX, there's a very popular transpiler (Babel's), but it's by no means the only one or even "the official one". The spec actually lists three, and mentions [1]:
> "These are a set of transpilers that all conform to the JSX syntax but use different semantics on the output: [...]"
JSX is most commonly used with React, so if you're making a JSX transpiler it makes sense to make your default output work with React. This is by no means part of the definition of JSX though or a spec requirement.
Do you mean that before JSX could be compiled to createElement and some other library's createElement could be used? If so I would ask how often does that actually happen?
I would assume that any library that wanted to consume JSX would have their own JSX parser and compile to whatever structure that library wanted?
on edit: I guess you went through that in the other comments, and your worries are at least partially handled by the changes to Babel?
Yes, this is quite common practice. Many non-React libraries support this, but it's also extremely easy to use with your own templating function (the implementation can be as little as 1 line long)
> I would assume that any library that wanted to consume JSX would have their own JSX parser and compile to whatever structure that library wanted?
Nope, this isn't how JSX works. For example, React does not contain a JSX parser. JSX syntax is transpired by a build tool: currently the two most popular build tools are TSC and Babel. Both support targeting a custom library or your own custom code instead of React (but only the function name can be customised, the function signature that JSX is transformed to, as discussed in this article, is not customisable)
I think the similarity between the children value and other props is useful, to emphasize how any prop can contain child JSX elements. There's nothing special to the children value besides some syntax sugar. You can pass JSX elements to any other prop too.
The React component API is not part of JSX (and has little bearing on it).
Sorry for bringing up this seemingly tangential question, but honestly the only thing I can think of when I see React stuff these days is that Facebook is behind it.
You can see that React development essentially stopped May 24 and then got sort back to normal by the end of June, but it's still a bit below normal levels. I think the cause was that many React devs became fed up with Facebook. I don't know how many actually quit though.
And I’m not so sure Facebook representatives have any ground to stand on when it comes to expectations of privacy.
Collecting publicly available information from the web to assess the state and power structure of one of the most widely-used projects on the web is not in any way whatsoever "stalking".
It's not even "sensitive" if it's publicly available.
1) Major updates of the actual Facebook product - I could see a lot of React resources being pulled into that.
2) Fiber implementation getting bogged down in the reality of implementing a production-ready cooperative multitasking scheme in the browser environment.
I have no evidence for that though - pure assumption.
JSX is really a way to describe a hierarchy of deferred function calls independent of their callers. This could be tremendously useful in a number of different contexts, not just UI. What would a state machine look like if the states could be described in JSX? What would rx.js look like if this mechanism was available?
The current tooling makes experimenting with possible alternate use cases pretty awkward, and so basically no one is doing work in this area.
I believe that these changes, particularly the `jsx` and `jsxs` auto import, will make it even harder to separate JSX as a concept from React and React-like libraries. It'll make it harder to standardise JSX, which is frustrating as it's the most supported ECMAScript syntax extension by a long way.
My modest proposal is that a JSX element desugars to a call to a block scoped identifier called `jsx`, so I can do something like this:
import { jsx } from 'react';
import { elementCreator } from 'somelib';
const someFunction = () => {
const { jsx } = elementCreator();
return <iAmNotAReactElement />;
};
const MyReactComponent = () => <div>cool</div>;
Taking this alternative approach would give developers a widely supported syntax extension that could allow for some really innovative solutions. The current path is going to do the opposite.I understand that there are perf optimisations that React can do if they control all of this. But I feel like they're looking at this too narrowly, from only the perspective of UI. They have the power to open up a lot more opportunities.
function MyThing() {
return (
el(A, {value: 123}, [
el(B, {value: 456, foo: true}, [
el(C, {awesome: true}),
el(D, {awful: false})
])
])
);
}
but there's a reason people prefer JSX syntaxSpeaking for myself, I rather this be an alternative so I can get the best perf on my React apps, but have the option to switch to this when experimenting with using JSX for other things. This is an interesting concept, you should build it (:
Also the decision to split React's createElement() into jsx() and jsxs() means that to maintain compatibility all libs would have to do the same, and provide both functions, all because of an implementation quirk in React.
JSX should be a standalone thing. It's not intrinsically React, and shouldn't bend to React's needs alone.
Reading the "Improvements over JSX" [1] I don't see anything that makes it worth the hassle (unless you really hate transpilers/tools). Most of them are outdated or not actual "Improvements".
[1]: https://github.com/developit/htm#improvements-over-jsxI do feel Typescipt tries to over-accommodate React compared to other frontend frameworks. The jsx code is pretty gnarly in TS and this makes it even more complicated for little gain.
Magic imports are a big no no. The whole idea about import/export syntax is that a module explicitly imports what it uses and other modules can only import what it exports.
I can understand React.createElement -> jsx function. Preact, mihthrill and others use the h function. This already works without new transform.
It seems that with React hooks and special JSX transforms, the authors are trying to prioritize cleverness over simplicity and clarity.
Now every project you have to grapple with the decision “is this automatic or classic?” and what about the next version, is it “super automatic” runtime?
function foo(props) {
return <h1>hi!</ht>
}
Becomes function foo(jsx) {
return function foo(props) {
return jsx.createElement('h1', ...);
}
}
Then, the first time a given component is rendered your view framework injects the right value of "jsx" and caches the resulting Component. This means no static dependencies are necessary to write components for libraries that use JSX.EDIT: added as comment https://github.com/reactjs/rfcs/pull/107#issuecomment-696978...
I wonder what plans the React team has for JSX. I liked the prop forwarding style that ReasonReact had, but I guess that's not compatible.
This is interesting; I take the opposite approach, thinking that if I can help someone understand that JSX is merely fancy syntax on top of plain JS function calls, that React won't seem so "magical".
I sometimes see comments like "I hate React's looping syntax" and it's clear that a lot of people get hung up thinking about React as a templating language.
I can see how explaining that extra level of detail could be confusing though, especially right up front, and especially for developers who're simultaneously learning JS alongside React.
Like this:
/** @jsx createElement */
import {createElement} from "@bikeshaving/crank";
import {renderer} from "@bikeshaving/crank/dom";
renderer.render(<div id="hello">Hello world</div>, document.body);
[1]: https://crank.js.org/guides/getting-startedHowever from my experience out in the day to day world (which is largely outside Bay Area Silicon Valley), this is not the case. Indeed, HNers and forward thinking devs here were the first comers to React, largely. I know I was on it pretty early (and walked a way more than once!) but the reality is its just picking up major steam in other places, the so called 'shadow developer' communities, like large .NET shops that when I talk to them at conferences, desperately want to move away from Angular, and React is often a choice (Vue is gaining steam here too. I think this second order of developers will decide how big each get, IMO).
Through that lens, its amazing they're taking these steps to find the balance between 'magic' (auto imports!) and lessening the syntax you have to learn as you grow development. There are so many shops, in enterprise in particular, full of 'shadow' developers most of us never meet or hear about, that are hungry for this kind of thing. Angular has some foot hold here, but Microsoft has been keen on trying to give alternatives, one of which is React (and MS here still has a lot of weight) and I think React will have staying power and growth by making itself more appealing to these kinds of developers. These are the kinds of devs who still use jQuery in 2020 (not that its bad! I swear I don't think that outright), even for new projects. There's a ton of the web ecosystem that has not and does not develop with these things in mind.
I have witnessed this with hooks. Hooks made React more approachable to development teams I worked with because class components had an unintended side effect - Developers made all of the logic encapsulated by the component inside methods - where extracting functions would have been easier to test and been far more modular - simply because thats OOP to most devs (right or wrongly). So you'd end up with 10K line classes that do all kinds of byzantine things with extremely tight coupling.
With hooks, functions become the pattern, and the library (can we just call it a framework already?) really molds the developer into best practices of extracting components and using component composition.
Basically, if you aren't just looking to reach the HN crowd, these changes are great. Thats been my experience so far. React has been growing in popularity at places I've worked and other developers I know see this too (many of which develop at consultancies, PHP/Wordpress shops, .NET shops etc) because of the change of paradigms as well as stability, of course, have made it much more maintainable.
This says very little of the overall web ecosystem, which still has a major churn problem (same for the ecosystem around React, too much churn and breaking changes, over minimal upsides, I've observed)
Since I'm rambling, I wonder what React would look like if Web Components V1 was a thing when it first came out.
I'm seeing a lot more Vue though, more than I did even 6 months ago, and I even saw some job openings for Svelte, in the last month and half (can't remember where though)
Thats pretty anecdotal though.
It's unfortunate that they don't expand on this. Is the reduction on the number of concepts simply that one doesn't need to import React? I doubt it. So what does this new transform enable in the future that will reduce the number of concepts you need to learn?
> Other than performance this is also about a long term simplification of number of concepts you have to learn to use React. In particular, forwardRef and defaultProps are no longer something special.