-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Spring
isn't deeply reactive (target
cannot be mutated)
#14840
Comments
Fixed: import { Spring } from "svelte/motion";
export class BrokenCounter { // <---------- No longer broken.
#counter = $state({ value: 0 });
#spring = Spring.of(() => this.#counter.value);
get count() { return this.#spring.current }
incr() { this.#counter.value++ }
decr() { this.#counter.value-- }
}
export class OkayCounter {
#spring = new Spring(0);
get count() { return this.#spring.current }
incr() { this.#spring.target++ }
decr() { this.#spring.target-- }
} Explanation: It is bad that the Spring class has been declared as generic because that makes people think that passing anything other than a number would still work. It does not. Springs can only be built to increase or decrease a number. If you nest the number in some arbitrary value, the Spring class cannot possibly know this. The fix above changes |
For the core team: In my opinion: - export class Spring<T> {
+ export class Spring<T extends number> { // Or just remove the generic parameter. - static of<U>(fn: () => U, options?: SpringOpts): Spring<U>;
+ static of<U extends number>(fn: () => U, options?: SpringOpts): Spring<U>; // Or remove U. Unsure why Spring/Tween accepts any data type. Feature suggestion:// New overload.
static of<U extends number>(getter: () => U, setter: (newValue: U) => void, options?: SpringOpts): Spring<U>; By adding a setter, people can write to |
Historically, I think The primary use-case seems to be for types like |
I wasn't aware of this. My bad. My response was based on the assumption that these could only work with numbers. I'll check the source code... |
AmmendmentBest to just get rid of T, I think? + type MotionPrimitive = number | Date;
+ interface MotionRecord {
+ [x: string]: MotionPrimitive | MotionRecord | (MotionPrimitive | MotionRecord)[];
+ };
- export class Spring<T>
+ export class Spring {
...
- constructor(value: T, options?: SpringOpts);
+ constructor(value: MotionRecord[''], options?: SpringOpts); Do similar thing to the static As for the bug: Is there any reference in the documents or any official examples that imply that increasing import { Spring } from "svelte/motion";
export class BrokenCounter {
#spring = new Spring({ value: 0 });
get count() { return this.#spring.current.value }
incr() { this.#spring.target = { value: this.#spring.target.value + 1 } }
decr() { this.#spring.target = { value: this.#spring.target.value - 1 } }
}
export class OkayCounter {
#spring = new Spring(0);
get count() { return this.#spring.current }
incr() { this.#spring.target++ }
decr() { this.#spring.target-- }
} |
I don't think this is a good idea 😅 For type-safety, the exact type needs to be known so that |
It is unclear to me what isn't a good idea. The typing I proposed is a mere reflection of what the code is currently capable of processing. |
If const mySpring = new Spring(0);
mySpring.target++; // An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type. (2356) Because TypeScript is unable to "remember" that the |
Ah I see. We then just need |
Describe the bug
I have something like this in my app:
Rather than re-assigning
target
to a new object, I directly mutate thetarget
object.Doing
target.value++
instead oftarget = { value: target.value + 1 }
causes theSpring
to "miss" the update. It surprised me because I assumedtarget
would be proxied by theSpring
(to make it deeply reactive), just like$state
. Plus,Spring
already "looks deeply" into thetarget
value (to animate individual properties and array items).Creating a new object and assigning it to
target
makes for a quick workaround, but it is a bit inefficient and in some situations will lead to a lot of unnecessary garbage being created! This might be a regression fromspring
, which allowed mutating its target through theupdate
method/function (I think?).I see three potential solutions to this issue:
Spring
(and maybe others?) deeply reactive just like$state
target
and issue a warning in the console (probably just in development)Reproduction
https://svelte.dev/playground/b9defdca3842490bb907fcfadaedfecc?version=5.16.0
Logs
No response
System Info
Severity
annoyance
The text was updated successfully, but these errors were encountered: