DEV Community

Atif Afzal
Atif Afzal

Posted on • Originally published at atfzl.com on

Understanding Solid: JSX

JSX was introduced by Facebook for complementing React in javascript. It is a common misconception that JSX is somehow coupled with React or its siblings like React Native, Preact, Inferno etc. But JSX is an extension to javascript and can be used in other places beside React.

Solid uses JSX to render vanilla DOM elements. In React the <div /> compiles to React.createElement('div') but in Solid you could say it compiles to document.createElement('div') (actually it uses HTML templates, more on this ahead).

Hello World component:

function HelloWorld() {
  return (
    <div>
      Hello World
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This will (conceptually) compile to:

function HelloWorld() {
  const el$ = document.createElement('div');

  el$.innerText = 'Hello World';

  return el$;
}
Enter fullscreen mode Exit fullscreen mode

Actually solid uses HTML template element because it is more performant for creating new instances from same template.

So it actually compiles to this:

function _$template(str) {
  const t = document.createElement('template');
  t.innerHTML = str;
  return t.content.firstChild;
}

const _tmpl$ = _$template(`<div>Hello World</div>`);

function HelloWorld() {
  return _tmpl$.cloneNode(true);
}
Enter fullscreen mode Exit fullscreen mode

From the previous post we know how solid is tracking dependencies. We’ll use it here now by creating a counter.

function Counter() {
  const [state, setState] = createState({ counter: 0 });

  setInterval(() => {
    setState({ counter: state.counter + 1 });
  });

  return (
    <div>
      {state.counter}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

This will compile to:

const _tmpl$ = _$template(`<div></div>`);

function Counter() {
  const [state, setState] = createState({
    counter: 0
  });
  setInterval(() => {
    setState({
      counter: state.counter + 1
    });
  });
  return function () {
    const _el$ = _tmpl$.cloneNode(true);

    createEffect(() => {
      _el$.innerText = state.counter;
    });

    return _el$;
  }(); // NOTE: this is an iife!
}
Enter fullscreen mode Exit fullscreen mode

Note that the string passed to _$template does not have the part where we had the dynamic value {state.counter}. It will be added later in createEffect.

Now whenever we update the counter, the createEffect block runs which updates the innerText of _el$.

JSX with nesting:

function Counter() {
  const [state, setState] = createState({ counter: 0 });

  setInterval(() => {
    setState({ counter: state.counter + 1 });
  });

  return (
    <div>
      <div>Counter:</div>
      <div>{state.counter}</div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Compiles to:

const _tmpl$ = _$template(`<div><div>Counter:</div><div></div></div>`);

function Counter() {
  const [state, setState] = createState({
    counter: 0
  });
  setInterval(() => {
    setState({
      counter: state.counter + 1
    });
  });
  return function () {
    const _el$ = _tmpl$.cloneNode(true),
          _el$2 = _el$.firstChild,
          _el$3 = _el$2.nextSibling;

    createEffect(() => {
      _el$3.innerText = state.counter;
    });

    return _el$;
  }();
}
Enter fullscreen mode Exit fullscreen mode

Note that the static part string: Counter: is left inside the template string passed to _$template We refer to the nested elements by using combination of firstChild, nextSibling etc.

And this is in short how Solid works. Effectively these two parts: dependency tracking and JSX provide the best of both worlds. We get the best performance by executing only the minimal code which is needed and with JSX we get to keep the mental model introduced by React which keeps us more productive.

Oldest comments (0)