DEV Community

Discussion on: 2021 JavaScript framework

Collapse
 
mindplay profile image
Rasmus Schultz

Solid doesn't transform any code outside of the JSX.

This is partially what I was hung up on - I was thrown off by some subtle differences between the counter example and the no compilation example:

One uses count to emit the value and the other uses count().

One does setCount(count() + 1) and the other does setCount(c => c+1).

I suspected these differences were due to some compile-time black magic, but it turns out, these are just different ways to do the same thing. (You might want to align these examples so they're more closely similar? it's difficult to judge the difference between compiled and non-compiled, when the differences aren't all relevant.)

I may have misjudged and gotten hung up on something that isn't there. 😅

I may have been wrong about the changing semantics as well - I think I've gotten so used to the semantics of the de-facto standard JSX transform that I had forgotten this wasn't part of JSX in itself... in my mind, JSX and the React transform, and the resulting semantics, was just a part of how JS worked - but it's obviously not.

So now I think I owe it a closer look. 😊

It's still not at all clear to me what the semantic meaning of JSX is with Solid's custom transform though - I still look at the compiled output code and struggle to understand what exactly a JSX expression means or is.

With the React transform, this is remarkably easy to understand after a quick look in the Babel REPL at some JSX output - whereas the output from the Solid transform is more like the compiled output of Svelte or something... I struggle to make a connection.

I'm not the sort of person who picks up things like this and happily accepts them as a "black box" that "just works" - I like things I can understand and explain.

Have you written anything about the transform? Or how would I go about trying to grasp the inner workings of this?

Thanks for you patience in explaining and discussing this, Ryan! 👍

Collapse
 
ryansolid profile image
Ryan Carniato

Yeah the counter with the c => c + 1 was just a style choice. Something React developers would find more familiar. It has the benefit of self referencing without creating a subscription (automatically untracks).

I've written a bit about the JSX transform: javascript.plainenglish.io/designi...
But often I suggest just looking at the Output tab from the REPL: playground.solidjs.com/.
While there is some optimization around grouping effects, event delegation, JavaScript ternary and boolean expression, and detection of static only expressions that don't need to be wrapped you can view the whole process basically like:

const d = <div onClick={increment}>{count()}</div>

// roughly compiles to:
const d = document.createElement("div");
d.addEventListener("click", increment);
createEffect(() => d.textContent = count());
Enter fullscreen mode Exit fullscreen mode

Mentally it creates real DOM elements and wraps expressions in Reactions(createEffect is like MobX autorun) to update the DOM. I like to think of it as roughly what I'd write if I wasn't using a template language. If you were to take the reactive system of your choice and try to create that element without tagged template literals what would you do?

This differs from something like Svelte which compiles your code into its internal component structure and distributes your code in class lifecycles. Solid system is just reactivity. The one arguable exception is the insert but it's written the way it is to avoid certain closures.. It really is:

function insert(el, fn) {
   createEffect(() => internalInsert(el, fn));
}
Enter fullscreen mode Exit fullscreen mode

The internalInsert is basically the closest equivalent to the HyperScript function except it is only concerned with insertion. What's cool about this approach is that outside spreads which (basically use the other part the HyperScript function) we know which attributes can change and just inline the effects in the compiled output. All little details but it both streamlines for performance and has this really nice benefit is most of end user code is included in a way that is easily traceable right in the compiled output.

In the end I just think of Components like factory functions where each dynamic expression closes over the state and then returns the native DOM element. This is basically the same as the HyperScript version except we shortcut all the checking of which attribute has changed.

Hopefully that makes sense.