In Solid, props and stores are proxy objects that rely on property access for tracking and reactive updates. Watch out for destructuring or early property access, which can cause these properties to lose reactivity or trigger at the wrong time.
From the docs; does SolidJS provide a way to lint or warn on this? I've been getting more and more scared of destructuring and ... copying recently since, for example, you lose prototype info when you do this. TypeScript doesn't warn you.
I’m working on eslint-plugin-solid for this reason, and while I haven’t implemented this yet (it’s complicated), it will eventually warn when something is wrong with reactivity in general.
I've been getting more and more scared of destructuring and ... copying recently
Well, certainly it should be clear that `obj !== {...obj}`, and you have to behave accordingly.
In reality though, assuming you are destructuring to pass it down a tree (and not around the app), this usually just means that you lose the optimizations of only rendering some part of the subtree and render your whole subtree more often, which is equivalent to using state management that isn't integrated into the scheduling engine very well, which is very common. So you're just going from a nicely performant app to standard fare.
I would recommend to always try to think in a singleton structure and use IDs and maps to the original objects over de- and re-constructing things you pass around as if they were the original
Well, certainly it should be clear that `obj !== {...obj}`, and you have to behave accordingly
Sure but that's not my issue. I'm saying that when you copy an object like you just did, the latter object looses its prototype.
So any prototype methods I try to call on a copy/de structured object like that will crash in my app without a prior TypeScript warning.
I want to be able to copy objects with a nice syntax but still have them retain their prototype.
Since no one warns you (TypeScript anyway) about losing your prototype it makes me worry about this everywhere because who knows which objects weren't meant to lose their prototypes.
> Well, certainly it should be clear that `obj !== {...obj}`, and you have to behave accordingly
Sure but that's not my issue
I think you misunderstand what I'm saying. I know you know `obj !== {...obj}`, but it's important to understand exactly what that means, and one of those things to understand is
when you copy an object [via destructuring], the latter object looses its prototype
For example, any class instance should most certainly not be used in that way, as it moves away from a true "object" paradigm (keys and values baby) into an inheritance paradigm, and as you observed inheritance is lost in destructuring.
TypeScript doesn't warn you
I find this hard to believe. If you are passing TypeScript some interface T, and the object {...tInstance} doesn't have the keys of T, you should get an error. If you are passing TypeScript some class X, and you try to claim `typeof {...(xInstance)} === X` you would also surely see errors.
Please link an example so I can understand what I mean, I would guess you didn't type the destination very stringently so the loss of the class type was unobserved
Destructuring only really makes sense for simple struct-like data structures. If you are worrying about prototypes you are destructuring the wrong things. Duplicating class instances is almost always something that you'll need to do manually or through serialization in OOP afaik.
It could potentially if we do analysis to identify components. It's kind of like React's rules though in that sometimes you want to access things in an untracked context on purpose. A simple linting rule that is ignorable would probably help.
Props are getters. They are shallow. It are stores that are actual ES proxies.
I think the term is used loosely here to suggest that they are wrappers on top of objects. It isn't so much about the specifics but to explain why destructuring should be avoided.
It's a fair question. Pre React Hooks I expected the proxy plain object approach to be more common. We definitely want consistency regardless if component consumer passes signal or literal, so I went that way. There are cases like with spreads where they are actual proxies too. But probably could have made all props are functions work and have them work with combination of proxies as well things just didn't play out that way.
Comments
From the docs; does SolidJS provide a way to lint or warn on this? I've been getting more and more scared of destructuring and ... copying recently since, for example, you lose prototype info when you do this. TypeScript doesn't warn you.
I’m working on eslint-plugin-solid for this reason, and while I haven’t implemented this yet (it’s complicated), it will eventually warn when something is wrong with reactivity in general.
Well, certainly it should be clear that `obj !== {...obj}`, and you have to behave accordingly.
In reality though, assuming you are destructuring to pass it down a tree (and not around the app), this usually just means that you lose the optimizations of only rendering some part of the subtree and render your whole subtree more often, which is equivalent to using state management that isn't integrated into the scheduling engine very well, which is very common. So you're just going from a nicely performant app to standard fare.
I would recommend to always try to think in a singleton structure and use IDs and maps to the original objects over de- and re-constructing things you pass around as if they were the original
Sure but that's not my issue. I'm saying that when you copy an object like you just did, the latter object looses its prototype.
So any prototype methods I try to call on a copy/de structured object like that will crash in my app without a prior TypeScript warning.
I want to be able to copy objects with a nice syntax but still have them retain their prototype.
Since no one warns you (TypeScript anyway) about losing your prototype it makes me worry about this everywhere because who knows which objects weren't meant to lose their prototypes.
I think you misunderstand what I'm saying. I know you know `obj !== {...obj}`, but it's important to understand exactly what that means, and one of those things to understand is
For example, any class instance should most certainly not be used in that way, as it moves away from a true "object" paradigm (keys and values baby) into an inheritance paradigm, and as you observed inheritance is lost in destructuring.
I find this hard to believe. If you are passing TypeScript some interface T, and the object {...tInstance} doesn't have the keys of T, you should get an error. If you are passing TypeScript some class X, and you try to claim `typeof {...(xInstance)} === X` you would also surely see errors.
Please link an example so I can understand what I mean, I would guess you didn't type the destination very stringently so the loss of the class type was unobserved
I can't reproduce what I'm saying! Well that's interesting. Thanks for pushing me.
I wonder what I was seeing in my code...
Cheers, good luck to figure it out.
Destructuring only really makes sense for simple struct-like data structures. If you are worrying about prototypes you are destructuring the wrong things. Duplicating class instances is almost always something that you'll need to do manually or through serialization in OOP afaik.
All I did was try to put some helper methods on data classes. :D But yeah clearly I'm going against the grain.
It could potentially if we do analysis to identify components. It's kind of like React's rules though in that sometimes you want to access things in an untracked context on purpose. A simple linting rule that is ignorable would probably help.
Properties are proxies, but getters should be used for everything else. Why weren't getters also used for properties?
Props are getters. They are shallow. It are stores that are actual ES proxies.
I think the term is used loosely here to suggest that they are wrappers on top of objects. It isn't so much about the specifics but to explain why destructuring should be avoided.
Yes, by getters I was trying to refer to these wrappers.
It's a fair question. Pre React Hooks I expected the proxy plain object approach to be more common. We definitely want consistency regardless if component consumer passes signal or literal, so I went that way. There are cases like with spreads where they are actual proxies too. But probably could have made all props are functions work and have them work with combination of proxies as well things just didn't play out that way.