Code Monkey home page Code Monkey logo

Comments (6)

hanszoons avatar hanszoons commented on July 18, 2024 3

The compiler/editor should at least warn when you are updating the non-reactive maps, sets etc. It does that already when you supposedly forgot to declare a variable with $state when you update it somewhere.

I have spent hours debugging a complex app, turned out i refactored out functions that use reactive maps to a seperate file that didn't import them. It is far, far too easy to forget this (and have your code fail silently).

from svelte.

Rich-Harris avatar Rich-Harris commented on July 18, 2024 2

Just to provide some reasoning for why things are the way they are (which isn't to say that they're set in stone):

  • It aids discoverability. Without reading the docs in their entirety, you could completely miss ReactiveSet et al, whereas if you (perhaps assuming that built-in sets somehow work this way) type new Set and are prompted to auto-import from svelte/reactivity, you're more likely to learn about them
  • It's clearer that the API matches the built-ins
  • We have an ambition, not yet written up in public, to add reactive variants of things like window (so that you can read window.innerWidth reactively without the indirection of <svelte:window bind:innerWidth={w} />, even outside components). I'm not sure what that would look like in this world. reactiveWindow? Kinda ugly!

There are some potential mitigations for the issues you describe. For example we don't have any inline documentation on these classes, and that could easily be fixed:

image

Slightly trickier, due to how the types get generated (methods that have the same signature as the super class get omitted when TS generates the .d.ts file), but we could probably figure out a way to make it work on methods as well:

image

Admittedly you don't get any indication on the instance itself, only its constructor and methods.

It would also be possible to monkey-patch built-ins at dev time so that if you do something like {set.has(x)} it warns you that you probably wanted to use the reactive Set rather than the built-in. Of course, that would yield occasional false positives so we would at minimum need a way to opt out:

<!-- svelte-ignore non_reactive_set -->
<p>{set.has(x)}</p>

(Today, svelte-ignore only controls compiler warnings, but we could make it work with runtime warnings too.)

from svelte.

david-plugge avatar david-plugge commented on July 18, 2024

100% agree. Runes make reactivity more obvious, the classes however do not

from svelte.

Antonio-Bennett avatar Antonio-Bennett commented on July 18, 2024

Why don’t you just rename it when you import it?

from svelte.

david-plugge avatar david-plugge commented on July 18, 2024

Do you also need to rename the state rune to make it obvious? Its just about understanding reactivity when you quickly ready through the Code. No need to check for an import. The simple let declaration in svelte Files where replaced by runes for this exact reason (and many others)

from svelte.

ryanatkn avatar ryanatkn commented on July 18, 2024

Thanks for the in-depth reply, I understand better now. I think the point made by @hanszoons is the most important implication of this choice. (I later edited it into the OP) But if monkey-patching in dev to warn on usage could catch all silent errors, I was wrong to say code cannot be moved across modules safely.

My personal preference would be to reduce ambiguity and avoid the headaches of shadowing globals, even with window, but my opinions have a history of trending the direction Svelte goes. I'm re-exporting for now but I don't want to go against the grain.

Some thoughts:

  • is shadowing globals a good practice in the long run?
  • do the learning and aesthetic benefits outweigh the costs?
  • it feels intrusive to have to write globalThis.Set when the reactive class is imported, or care about what's imported when using maps/sets/etc (though I think the downsides of getting it wrong will usually just be a small perf hit, but there's an ick factor - speculating, I think with shadowing we'll see over-use of the reactive collections in non-reactive contexts)
  • the monkey-patch will add friction for users who choose to alias to silence the warnings unless it's somehow detected or added as an option
  • maybe incompatible with runes for other builtins but $window looks better than reactiveWindow

The reactive version is currently assignable to the builtin, but this can be changed by extending the globals with TypeScript, although sometimes you may want the reactive version to be assignable to the builtin, like in helpers:

TypeScript REPL with assignment change

view TypeScript assignment change example
// Pretend this was imported from 'svelte/reactivity':
export class SvelteSet<T> extends Set<T> {
	// Existing properties like this make `Set` not assignable to `SvelteSet`:
	#sources = new Map();
	// Add a conflicting property to make `SvelteSet` not assignable to `Set`.
	// @ts-expect-error - this error is silenced because otherwise we couldn't extend the builtin `Set`
	readonly [reactive]: true; // comment this out to see the desired errors go away
}

// Extend the global with a conflicting type to make `SvelteSet` not assignable to `Set`:
declare global {
	interface Set<T> {
		readonly [reactive]: false; // comment this out to see the desired errors go away
	}
}
declare const reactive: unique symbol; // one way to "brand" types

export const a = new Set<string>();
export const b = new SvelteSet<string>();

export const b_from_a: SvelteSet<string> = a; // errors as desired without the brand
export const a_from_b: Set<string> = b; // requires the brand to get this error

takes_b(a); // errors as desired without the brand
takes_a(b); // requires the brand to get this error

function takes_a(a: Set<string>): void {}
function takes_b(b: SvelteSet<string>): void {}

This assignment change fixes some problems but causes new ones, like functions that don't care if it's reactive would require casting. There may only be downsides if the monkey-patching catches the same kinds of errors.

(aside: TypeScript 5.5 reports the unalised ReactiveSet in the error messages, not the exported Set or locally aliased SvelteSet, just another tooling quirk to fix or accept - the simple fix is removing Reactive, but is that desirable?)

from svelte.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.