S
SolidJS10mo ago
Dakotys

Why is it considered wrong to destruct props?

Why is it considered wrong to destruct props (https://docs.solidjs.com/concepts/components/props#:~:text=of%20the%20property.-,destructuring%20props,-Props%20are%20read), even thought the reactivity can be retained by just not calling the signal immediately when passing it and just passing reference and calling it only by last children or wrapping it in Accessor and modifying it as it would be needed. And why is there no standard for naming signals like const [_name, setName] = createSignal("Jakob"); or const [$name, setName] = createSignal("Jakob"); so it would be easier to distinguish that it is signal and that it needs to be called.
16 Replies
Raqueebuddin Aziz
It's just a const you are free to follow whatever standard you want 🙂
<Alterion.Dev>
<Alterion.Dev>10mo ago
Well, you can "maintain reactivity" by doing a workaround, sure, but the real way to get props and maintain things correctly, is to use splitProps. https://www.solidjs.com/tutorial/props_split?solved And I'm not sure why you need to have a specific indicator that something is a signal - with proper typings and linting, there's no reason to identify the type of variables, we're way passed the dark age of "stringMyString"
Dakotys
DakotysOP10mo ago
You can do the same with just this in https://www.solidjs.com/tutorial/props_split?solved ( the name is not called): import { render } from "solid-js/web"; import { createSignal } from "solid-js"; import Greeting from "./greeting"; function App() { const [name, setName] = createSignal("Jakob"); return <> <Greeting greeting="Yo" name={name} style="color: teal;" /> <button onClick={() => setName("Jarod")}>Set Name</button> </>; } render(() => <App />, document.getElementById('app')); and then in greeting.jsx : import { splitProps } from "solid-js"; export default function Greeting({greeting, name = ()=>'Mark', ...rest}) { return <h3 {...rest}>{greeting} {name()}</h3> } And you can even set default props.
<Alterion.Dev>
<Alterion.Dev>10mo ago
My memory is a little fuzzy on the specifics here, but I seem to recall that it looks like it works correctly here, but the moment you start writing actual code, more complex code, this will break down pretty quickly attempting to get around how you're shown in the tutorial is a great way to create weird side-effects and unexpected behaviours later down the line.
Dakotys
DakotysOP10mo ago
I don't think this can cause any problems, since it is just passing reference, it just allows to destruct props and it also makes use of mergeProps and splitProps redundant, the only thing that it is missing is common naming standard, so it would be clear that it is Acessor and that the value needs to be called.
<Alterion.Dev>
<Alterion.Dev>10mo ago
I hereby relinquish the answer of this question to someone more expert than I in the reason why this is the way it is. :kek:
Otonashi
Otonashi10mo ago
if you are passing accessors then you can destructure props fine, just be careful that you aren't destructing a reactive prop and expecting it to update on the other hand, the convention in solid is to allow the jsx transform to compile props into getters instead of passing accessors with the example you're using, this for instance would not work if you were destructing props
function App() {
const [name, setName] = createSignal("Jakob");

return <>
<Greeting greeting={"Yo " + name()} name={name} style="color: teal;" />
<button onClick={() => setName("Jarod")}>Set Name</button>
</>;
}
function App() {
const [name, setName] = createSignal("Jakob");

return <>
<Greeting greeting={"Yo " + name()} name={name} style="color: teal;" />
<button onClick={() => setName("Jarod")}>Set Name</button>
</>;
}
(well you probably wouldn't want to actually have name twice but it gets the point across) but yes end of the day it is just a convention and if you prefer the naming approach to identify accessors that's fine too just be aware that the built-in components follow the getter convention, and that anyone using your components is aware of the difference
Dakotys
DakotysOP10mo ago
But this is only an edge case, where you would pass concatenation of string + prop, and event then just wrapping it in accesor solves it, function App() { const [name, setName] = createSignal("Jakob"); return <> <Greeting greeting={()=>"Yo " + name()} name={name} style="color: teal;" /> <button onClick={() => setName("Jarod")}>Set Name</button> </>; }
Otonashi
Otonashi10mo ago
if you're asking why it is this way i can't really answer that
Dakotys
DakotysOP10mo ago
I understand, that is just my inner React child talking
Otonashi
Otonashi10mo ago
but it is something that comes up from time to time so you can try searching
fabiospampinato
fabiospampinato10mo ago
@Dakotys if you want to just pass signals around that's what I'm doing in Voby: https://github.com/vobyjs/voby, there's no "hard" reason for doing it differently other than Ryan liking this the way it works in Solid, basically. The closest "strong" reason is that Solid supports proxy props, which is a complicated kinda obscure feature that allows the framework to transparently support a very dynamic way of passing props to components. If you don't need that then you are not missing out on anything, if you need that arguably you shouldn't need that in the first place because it causes performance issues when used heavily, but potentially it could still be supported without any custom transform by just explicitly constructing this proxy and manually passing it to the component. Basically if you just pass signals around and you write normal code you can just do destructuring, use default values, delete splitProps/mergeProps etc. As far as naming conventions go I think the explanation is that that would only be useful for people new to signals, like when I first started I also tried to mark signal identifiers specially, but over time that becomes just visual clutter, so naturally over time special markers on identifiers for signals get deleted as they become unnecessary. Another "strong" reason is that as I understand it historically Solid was built around using stores for managing state, rather than createSignals, so you would normally embed properties from a store in the JSX, problem is that if you do <div>{store.value}</div> that can't simultaneously be reactive and return you a number, so that has to be wrapped in a function: <div>{() => store.value}</div>, which gets inconvenient and error-prone quickly, so Solid has a custom transform that works the way it does largely for this reason also. Thing is, the "default" state management solution is not stores anymores but signals, so you can just write <div>{value}</div> because now value is a function already, and funnily enough that ends up being cleaner than <div>{value()}</div>. Basically what you should be doing largely depends on what kind of code you like writing I guess. Solid comes from an, imo, strange/antiquated position, where historically state was managed via stores, and code was written in javascript. Today state should probably largely be managed by signals directly, and most importantly we are relying on types to catch issues with our code, in that world just passing signals around is arguably superior because the code ends up being cleaner on average (like you can take advantage of shorthands also potentially: class={{ foo, bar }}, rather than Solid's classList={{ foo: foo(), bar: bar() }}) and more correct type-wise (TS understands that we are passing a function around and not a number, and a component can express if it doesn't support reacting to a prop, by just saying that it doesn't accept functions for that prop, which is actually useful in some situations, random example: if you have a VirtualList component that supports multiple virtualization strategies it may just not make any sense to be able to support switching between them, if you wrote that component in Solid your component just can't say that it doesn't support that, and whoever uses this component can't know that that's not supported by just looking at the interface of the component)
Dakotys
DakotysOP10mo ago
Thank you for your thorough explanation 👏 . And i had no idea that you could directly use signals in JSX.
fabiospampinato
fabiospampinato10mo ago
And i had no idea that you could directly use signals in JSX.
In Solid you mean? Like doing <div>{signal}</div> in Solid?
Dakotys
DakotysOP10mo ago
Yeah
fabiospampinato
fabiospampinato10mo ago
It's a tricky situation 🤣 basically that kind of stuff must be supported, because something that can change must be a function, and a useful framework must be able to render things that can change, so you must be able to pass these functions around, like that has to be supported by the framework, but the thing is that in some cases a component can support having either a signal or a normal function as child, like Show for example, but Solid hasn't given itself a way to tell signal functions apart from other functions, so the Show component technically just can't be implemented correctly if you did <Show ...>{child}</Show> because the component can't know if child is a signal or a callback, basically, so in order to bypass errors like that the type of JSX.Element is explicitly wrong, it says that functions are not supported when they are, so basically when you can actually just pass a signal to a native element without calling it TS will tell you that you can't do it, even though you can in practice, and when you must actually return a function, like when implementing something like a Show component yourself, you have to assert that that's not actually a function to make sure the type of what you are returning is assignable to JSX.Element, which again is wrong on purpose
Want results from more Discord servers?
Add your server