S
SolidJSβ€’5mo ago
TomDaBomb

Tagged Template Literals - Why is unwrapped props value reactive?

The docs at https://github.com/solidjs/solid/tree/main/packages/solid/html indicate that reactive variables must be manually wrapped when using tagged template literals, yet this basic example still works, https://playground.solidjs.com/anonymous/45f46e73-ac35-4455-a03b-f8501027fe23 Clarification: I am referring to line 25. It is unwrapped yet is still reactive. I was just curious, why is this? Expected behavior: Label2 shouldn't be reactive. Actual behavior: Label2 is reactive.
GitHub
solid/packages/solid/html at main Β· solidjs/solid
A declarative, efficient, and flexible JavaScript library for building user interfaces. - solidjs/solid
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
29 Replies
peerreynders
peerreyndersβ€’5mo ago
Change it to
function App() {
return html`
<div>
<${Label1} value=${count} />
<${Label2} value=${count} />
</div>
`;
}
function App() {
return html`
<div>
<${Label1} value=${count} />
<${Label2} value=${count} />
</div>
`;
}
and see what happens https://codepen.io/peerreynders/pen/eYoaoXV?editors=0010 The example passes props to the component as unwrapped functions, not wrapped functions.
TomDaBomb
TomDaBombβ€’5mo ago
Clarification: I was referring to line 25. It is unwrapped yet is still reactive. (updated my question)
No description
TomDaBomb
TomDaBombβ€’5mo ago
NOTE: There is even a warning to wrap it, but it actually still works. And, I thought this was very interesting and was curious about what was happening here.
No description
bigmistqke
bigmistqkeβ€’5mo ago
It's re-executing the component, which also causes all the html-elements to be created again on each prop change.
bigmistqke
bigmistqkeβ€’5mo ago
it would be the same as
function Label3(props) {
const value = props.value
return <div>{value}</div>
};
function Label3(props) {
const value = props.value
return <div>{value}</div>
};
This also 'works' in the same sense as your example: it will be reactive, because the component re-executes on every props.value change. But it will also re-create a div-element everytime. see https://playground.solidjs.com/anonymous/8e54e752-52d6-4e34-8d83-bb9d1c335655
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
peerreynders
peerreyndersβ€’5mo ago
I was referring to line 25
I was aware of that. I pointed out where your example deviated from the instructions. https://github.com/solidjs/solid/tree/main/packages/solid/html#readme
return html`<${Button} type="button" onClick=${increment}>${count}<//>`;
return html`<${Button} type="button" onClick=${increment}>${count}<//>`;
count is a signal and increment a derived value. Both of them are passed as unwrapped functions. Your example passes a function that returns the unwrapped function (unnecessarily). Back to the instructions about wrapping:
html`<div id=${() => props.id}>${() => firstName() + lastName()}</div>`
html`<div id=${() => props.id}>${() => firstName() + lastName()}</div>`
Observations: 1. It's a <div>, a leaf node, so reactive values can't be passed down any further this is where the value they represent actually needs to appear. 2. firstName() + lastName() composes two values (presumably this is what β€œreactive expression” means). This needs to be wrapped in a function. 3. props.id is usually a getter for a primitive value, not a function. So for the whole subscription mechanic to work it needs to be wrapped in a function.
GitHub
solid/packages/solid/html at main Β· solidjs/solid
A declarative, efficient, and flexible JavaScript library for building user interfaces. - solidjs/solid
peerreynders
peerreyndersβ€’5mo ago
Without looking at the source code the rule seems to be if something is a function β€œrun it to get the value out of it and subscribe for further changes” (perhaps even recursively). - firstName() + lastName() isn't a function so we need to turn it into one. - props.id isn't a function (and doesn't return one) so we need to turn it into one to get the value out of it. In your code
function App() {
return html`
<div>
<${Label1} value=${() => count} />
<${Label2} value=${() => count} />
</div>
`;
};
function App() {
return html`
<div>
<${Label1} value=${() => count} />
<${Label2} value=${() => count} />
</div>
`;
};
with () => count you somehow managed to deliver a function via props.value which by the rules hypothesized would be run.
return html`<div>${props.value}</div>`;
return html`<div>${props.value}</div>`;
If you would have followed the instructions by the letter you should have written
function App() {
return html`
<div>
<${Label1} value=${count} />
<${Label2} value=${count} />
</div>
`;
};
function App() {
return html`
<div>
<${Label1} value=${count} />
<${Label2} value=${count} />
</div>
`;
};
Which results in exactly the behaviour that the instructions predicted. The other potential issue was that you were using a "no-build" approach in a "build" playground which may mysteriously "fix" certain errors (which wasn't the case here; but that is why I chose codepen, not the playground).
peerreynders
peerreyndersβ€’5mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
bigmistqke
bigmistqkeβ€’5mo ago
oops ur right, i was too fast w my answer
with () => count you somehow managed to deliver a function via props.value which by the rules hypothesized would be run.
this is the correct explanation: they are in fact wrapped, but done at the props-level instead of inside the template.
peerreynders
peerreyndersβ€’5mo ago
I'm not intimately familiar with the source code; I just wondered if I needed to adjust my mental model.
bigmistqke
bigmistqkeβ€’5mo ago
no i think u were spot on!
peerreynders
peerreyndersβ€’5mo ago
"Blind chicken" and all that πŸ™‚
TomDaBomb
TomDaBombβ€’5mo ago
@peerreynders ahhh, I see what you were doing now. Yes, line 25 indeed does not work as I originally hypothesized after changing the way the prop is passed in. πŸ™‚ I assumed that a getter, in itself, was still a "reactive expression" and hence needed to be wrapped. But, I suppose that is not the case! Also, @bigmistqke, my impression was that Solid doesn't re-render entire components. Are there exceptions to this? πŸ‘€ Also, @peerreynders, in the example you presented with props.id, if props.id is a getter, isn't a getter (to a primitive value) still a function? I think it's odd that a signal getter doesn't have to be wrapped, yet when a props value is assigned a signal getter, it has to be wrapped. Strange to me... I think it's fair to say that I have a gap in my knowledge about reactivity. πŸ€” ^ I'm also foregoing using build tools which may complicate things a bit (for learning purposes) (typescript is certainly useful for productivity, but I'm also a bit of a vanilla purist at heart πŸ˜†). PS thanks for the examples πŸ™‚ appreciated!
peerreynders
peerreyndersβ€’5mo ago
if props.id is a getter, isn't a getter (to a primitive value) still a function?
const target = {
get id(){
return 'THX1138';
}
};
console.log(typeof target.id); // "string"

const handler = {
get(target, prop, receiver) {
return target[prop];
},
};

const prop = new Proxy(target, handler);
console.log(typeof prop.id); // "string"
const target = {
get id(){
return 'THX1138';
}
};
console.log(typeof target.id); // "string"

const handler = {
get(target, prop, receiver) {
return target[prop];
},
};

const prop = new Proxy(target, handler);
console.log(typeof prop.id); // "string"
https://www.typescriptlang.org/play/?#code/FAYw9gdgzgLgBDAhgJwOYFN4F44G9hxwbwCWAJgBQCU+hhymArshHAOQAqAEgBoCMfAMwAONgG4CcAL7ApE0JChgANugB0ysKgowAngAd0YAGYIUxNeSpi4AeltwARLGQkIqR8AXR4AC0QQZKrIcDi0RJg65pgANHD6yGD6cQwg6CQAbujIVHiS9EwsZmiYANoJSQC6EoRSMbLy4D7xifqhcBDoAO5wAAqJAB66USUwcf6BwdZeTUqqGlo6BkamFfqWZNZ2Ds4wru6OQA
TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
peerreynders
peerreyndersβ€’5mo ago
I think it's odd that a signal getter doesn't have to be wrapped,
It doesn't have to be wrapped when you are passing it as a prop; it's simply the means to acquire (and subscribe) to the value; in that sense it's like a callback. When you need the actual value that it represents you need to wrap it: a) to get the value to do something with b) to subscribe to any changes that may be happening later to the value.
Want results from more Discord servers?
Add your server