3 ms·
It's funny that you mention Lit, because, to me, it is a good example of why classes are the wrong abstractions for UI. My go-to example is the Reactive Contro
by SebastianKra 2y ago
It's funny that you mention Lit, because, to me, it is a good example of why classes are the wrong abstractions for UI.
My go-to example is the Reactive Controller [1]. Some of the following concerns may seem minor in this simple example, but from experience with similar class based Frameworks, I know that they significantly contribute to accidental complexity as the interface grows:
- secondary state such as the timer handle pollutes the class members since the setup- and cleanup functions are separate members rather than the former returning the latter.
- it doesn't handle the timeout changing. Even in this tiny example, that would require bit of a refactor.
- state is declared far away from where it is actually needed.
- it takes about twice as many LOCs as the same code in React. Explicit code is not necessarily bad, but even in this small example it detracts from the readability compared to the React version [2].
- we need to manually trigger the re-render rather than the app keeping track of it for us.
- ...and where other Frameworks can at least argue performance since their reactive primitives are usually more fine-grained, Lit can't. Every controller can only trigger a re-render of the entire component. It might actually be worse, since there's no built-in way for memoization.
I will admit that, at first glance, the class system has an universal appeal. It's familiar and does indeed seem easier to reason about as you can clearly see the relationship between events and subsequent changes. But the only reason why we turned to other abstractions in the first place, was that classes turn to spaghetti really quickly as you have to manually keep track of the exponentially increasing amount of state transitions.
On a related note, I'm (genuinely) unsure if classes were a good abstraction in the first place. They come with a ton of extra keywords and concepts in every language. And in Typescript, their use-cases can usually be accomplished with functions, closures and type-inference. They work well for storing state in memory, but we don't usually need the state in memory. State is usually stored in databases, local-storage, search params etc... In-memory state is frequently used for caching purposes, and these are can be easily abstracted away.
[1]: https://lit.dev/docs/composition/controllers/ https://lit.dev/docs/composition/controllers/
[2]:
export function useClock(timeout: number) {
const [value, setValue] = useState(new Date())
useEffect(() => {
const timerId = setInterval(timeout, () => {
setValue(new Date()
})
return () => clearInterval(timerId)
}, [timeout])
return value
}