This `<let/variable=...>` and `<for ...>` syntax is awful.
This `<let/variable=...>` and `<for ...>` syntax is awful.
<for ...>
In JSX I write JavaScript that returns JSX:
{data.map(node => <Element {...node} />)}
^ ----- JS ----^ ^ ------ JSX -------^
or
const elements = data.map(node => <Element {...node} />) ... <div>{elements}</div>
Really the most obscure syntax there is the splatting but it makes sense to you when you realize that JSX is just syntactic sugar for JS:
data.map(node => React.createElement(Element, { ...node }))
For Marko, that doesn't seem to be the case, but it also doesn't really make sense given the problems that Marko is trying to solve.
Another thing people might mean by "it's just JavaScript" is a much more subjective notion about how similar the overal syntax, control flow, etc. feels to whatever previous JavaScript experience the person has. This meaning is a lot harder to pin down, and in most cases reasonable people could disagree on what is and isn't "just JavaScript" according to this meaning. That said, I would tend to agree that React's templating uses normal JavaScript control flow and composition primitives more so than Marko.
What people mean when they say “React is just JavaScript” is…
1) JSX, more than any other templating system, is just HTML interleaved with JavaScript. It’s HTML, and anything between { and } is evaluated as JavaScript.
2) Inserting a React component’s “HTML tag” in your JSX is _actually_ the same as calling the JavaScript function. The HTML attributes are the function arguments. Yes, inside your function there can be state, and there can be contexts, and there are refs. But you get at all of those things by calling JavaScript functions.
Like,
<b><MyComponent attr=“yes” /></b>
is literally identical to: <b>{MyComponent({ attr: “yes” })}</b>
It’s the tiniest bit of syntactic sugar.I feel like too many people think “React is Just JavaScript” is some kind of lie people tell to make React sound cool.
It’s not a lie. There’s a _small_ amount of hand waving around the word “just” but the point is, it’s WAY smaller than what you need to explain the ways Svelte or Vue or Angular diverge from plain JavaScript.
This is unlike a lot of other templating languages where, even if the expression part of the language is pure JavaScript (or PHP or Python or whatever), it's still interleaved with arbitrary text which will get printed out according to its own rules. This makes the whole thing much harder to reason about (leading to the philosophy that you should put as little logic as possible in your templates because it makes them harder to understand, _even when that logic is directly related to the templating process_.
A good example is for-loops. In a lot of templating languages, you start in text-land, then you enter expression-land to write the opening {% for (const X of ...) %} line, then you're back in text-land again. You sprinkle in a couple of expressions, and then at the end you go back to expression-land to close the loop. You're jumping backwards and forwards between the two worlds, but there's no real syntactical or structural support for mixing them.
Meanwhile, in JSX, you start in text-land, then you open up an expression between two curly braces, and in that expression you write the entirety of your loop. If you need to go back to text-land within that loop, you can create a _new_ set of text nodes, but you're not interleaving expressions and text in the same way.
The result of this is that, once you understand how your JSX will get compiled, it's very easy to read it as if it were the JavaScript that it will get compiled to, rather than as a separate templating language. Which in turn makes it very easy to think of it as "just JavaScript", even if it's technically a syntax extension.
<b>{jsx(MyComponent, { attr: "yes" })</b>
(Previously this function was called "React.createElement", but these days they have special functions that only the JSX compiler is allowed to use.) The extra layer of indirection is needed to do things like support hooks being called inside of MyComponent's function body, keep track of `key` props, and so on.Regardless of whether you use JSX or createElement, you can't just call MyComponent({ attr: “yes” }) directly, is the main point.
JSX (react flavor) is lazy. <My component> is not rendered/evaluated until it's needed.
You can't just call a react component. It's not a regular function call.
That’s not true though and IMO is one of the weaknesses of JSX: it looks like something it is not. Having to use className instead of class is one of the most obvious tells. But in theory if it was just HTML with {}s I should be able to do:
<{tagName} />
<span {someStringWithAttributes} />
<div>{stringContainingHTML}</div>
and many other things you’re actually not able to do. Not to mention that things like onClick aren’t actually HTML attributes and are instead event listeners, etc etc.Once you grasp that what you’re actually doing is a function call with an object of arguments it makes sense. But it isn’t HTML. It’s a chain of function calls.
We’re all really used to it so we don’t think about it a lot. But I always try to remind myself of that when I look at a new unfamiliar syntax.
(not to mention, your example isn’t correct! <Component/> don’t map to Component(), it maps to previouslyUnknownFunction(Component()), which is another confusing factor)
<MyComponent prop={value}></MyComponent>
is just jsx(MyComponent, { prop: value })
Libraries and apps can use JSX or JS syntax. var ListComponent = () => {
let count = 0, selected = null
return {
view: ({attrs: {items}}) =>
m("div", [
m("p", "Clicked: " + count + " times"),
m("ul", items.map(item =>
m("li", {
onclick: () => { count++; selected = item },
style: {cursor: "pointer", color: item === selected ? "blue" : "black"}
}, item)
)),
selected && m("p", "Selected: " + selected)
])
}
}Speaking of writing JS instead of JSX or your example, I like the vanjs.org approach:
const Hello = () => div(
p("Hello"),
ul(
li("World"),
li(a({href: "https://vanjs.org/"}, "VanJS")),
),
)
van.add(document.body, Hello())But that was also back in the days when trailing commas at the end could break things, JavaScript editor support was relatively poor, and tooling wasn't where it is now (knowing your code is once again valid because the autoformatter just kicked in).
I have found aberdeenjs a better dx than hyperscript.
import { createElement as m } from "your-jsx-compatible-library";
var ListComponent = () => {
let count = 0, selected = null;
return {
view: ({ attrs: { items }}) =>
m("div", null,
m("p", null, "Clicked: " + count + " times"),
m("ul", null, items.map((item) =>
m("li", {
onclick: () => { count++; selected = item; },
style: { cursor: "pointer", color: item === selected ? "blue" : "black" },
}, item)
)),
selected && m("p", null, "Selected: " + selected)
)
};
}; <ul>
{% for user in users %}
<li>{{ user.firstName }}</li>
{% endfor %}
</ul>
And we have good syntax for templates now: <ul>
{#each users as user}
<li>{user.firstName}</li>
{/each}
</ul>
Why do we have to squish everything into HTML-like-but-not-quite blocks?But no, JSX isn’t that great either:
<ul>
{users.map(user => (
<li>{user.firstName}</li>
))}
</ul></each> is about control flow, so it's a conceptually different thing.
I can't tell how convincing that argument is to be honest, and how much I'm just rationalising familiarity.
Where they aren't two different things, you have some kind of component configuration language, like WPF on .NET. There is no control language, the instantiated components have behaviours and are responsible for any control logic, like rendering a list of items. This isn't a template language though.
HTML now has web components and so you can think of it in the latter way. I'm not sure if anyone has take this approach though.
But as I just said, in the end their function actually doesn't seem overly different (to me at least) so a visual distinction with curly brackets rather than angle brackets is perhaps not necessary.
SGML (XML, HTML) is for content (text) authors not developers, but webdevs just don't get it and look at it as a solution to developer problems.
I like vue a lot more;
<ul v-if={users}>
<li v-for={some in users}>{some.name}
</ul> <ul #if={users}>
{#each users as user}
<li>{user.name}</li>
{/each}
</ul>It started as a project at eBay and then got spun out to an open project around 2015
BTW - with Vue you can use entirely JSX of you dislike HTML component syntax (I don’t know enough about Svelte to know if it allows the same).
It was my reason for switching to React when I learned TypeScript after getting more into JS frameworks via Vue.JS.
My starting point was Vue 2.7, and I started out using string templates haha :)
Even wrote some reactive custom code (without Vue, just regular DOM code) in a customer widget that utilized Object.defineProperty et al, inspired by Vue.
And today, while I'm using React at $job, I also think Vue 3 is probably a solid framework as well.
Last time I checked, they improved on DX of their component and templating system. But I'm not even sure whether they still recommend the v-if etc helper tags.
For what it's worth, even Vue 2 always also supported JSX and later TSX