Reputation: 737
I think I understand Signals, Reactions, Derivations quite well in terms of Reactivity.
However, I can't understand how all of a sudden, putting them into an App()
function and the Signals can be updated into the DOM repeated when the Signals are updated?
I think perhaps this doc explains it, but it seems too complicated to understand. Can it be explained in a couples of paragraphs?
For example, I'd imagine
<div>{value()}</div>
where value()
comes from
const [value, setValue] = createSignal(123);
would have some code to have some Reaction that reacts to the Signal value
's change and update the DOM, but how did it happen? Is this createSignal()
the same as the regular createSignal()
or is it a special edition that sets up a Reaction to update the DOM? Also related is, since App()
is only run once, then how come the render()
needs to take () => <App />
but cannot just take <App />
as its first argument? If App()
runs once only, that means () => <App />
runs once only. So then why not just simply give a <App />
?
Upvotes: 2
Views: 1600
Reputation: 13698
Components are compiled into effects that gets re-evaluated when the signal's value is updated. Let me explain it briefly:
createSignal
creates a subscribers list and returns a pair of functions: a setter and a getter. Every signal keeps its own subscribers list.The setter function sets the next value, while the getter function returns the stored value.
So, reading a value, in other words invoking the getter as value()
, automatically inserts an effect into the signal's subscribers list.
This effect can be created via createEffect
maunally or automatically when JSX
component gets compiled into a function call.
So, when the signal gets updated, it is these effects that gets executed.
Lets see how components gets compiled into effects:
import { createSignal } from 'solid-js';
export const Counter = () => {
const [count, setCount] = createSignal(0);
return <div>{count()}</div>
};
The compiled output for the above component is as follows:
import { createSignal } from 'solid-js';
import { template as _$template } from "solid-js/web";
import { insert as _$insert } from "solid-js/web";
const _tmpl$ = /*#__PURE__*/_$template(`<div></div>`, 2);
export const Counter = () => {
const [count, setCount] = createSignal(0);
return (() => {
const _el$ = _tmpl$.cloneNode(true);
_$insert(_el$, count);
return _el$;
})();
};
See the line _$template(`<div></div>`, 2)
and _tmpl$.cloneNode(true)
and _$insert(_el$, count);
.
Here:
_$template
function creates a template reflecting the component's DOM structure.
cloneNode
clones the template.
$insert
function populates the values and inserts the returned DOM element into its parent DOM element.
So, it is not the getter's invocation but the re-running the whole effect that has the getter invocation updates the DOM.
How come the render() needs to take () => but cannot just take as its first argument?
The documentation answers your question. Solid's runtime keeps tracks of who owns the whom in order to prevent memory leaks. Passing a function is needed for settings things up.
It's important that the first argument is a function: do not pass JSX directly (as in render(, ...)), because this will call App before render can set up a root to track signal dependencies within App. https://www.solidjs.com/docs/latest#render
So, you can see <App />
as a function invocation, or more accurately as the invocation of the App
function. So, you will be feeding a value to the render
function.
If you don't like () => <App />
syntax, you can pass App
function like so:
const dispose = render(App, document.getElementById("app"));
The important thing is you provide a function that returns a JSX element which will be used as the root element of your application.
Upvotes: 5