DEV Community

Cover image for The Cost of Consistency in UI Frameworks

The Cost of Consistency in UI Frameworks

Ryan Carniato on July 12, 2022

Sometimes there are problems that have no universally good solutions. There is some tradeoff to be made. Some perspectives that can't be protected....
Collapse
 
peerreynders profile image
peerreynders

So honestly, this all sucks.

Really?

I'm pretty sure all four are internally consistent.

When it comes to solution approaches there is no universal model to consistency ("All models are wrong, but some are useful", remember).

What is important is that once you are equipped with the mental model that is appropriate for the framework/library that everything is predictable.

The Principle of Least Astonishment (POLA) isn't an unassailable law; it fails to acknowledge that an individual's level of surprise is directly correlated to their past individual experience. So what level of "majority experience" is required for POLA to be valid?

JavaScript is most despised because it isn’t some other language. If you are good in some other language and you have to program in an environment that only supports JavaScript, then you are forced to use JavaScript, and that is annoying. Most people in that situation don’t even bother to learn JavaScript first, and then they are surprised when JavaScript turns out to have significant differences from the some other language they would rather be using, and that those differences matter.

[Douglas Crockford, JavaScript - The Good Parts, 2008; p.2]

For decades JavaScript has aggravated developers worldwide because it didn't meet the expectations set by their first or favourite language. That didn't stop JavaScript from "eating the world". If JavaScript surprises you then your "mental model of JS" is faulty.

Similarly when switching between frameworks/libraries it should be expected that adjustments to one's mental models have to be made. (OK, that part sucks but that's the cost of doing business.) Ideally any tool should adopt the internal consistency model that allows it to operate the most efficiently and effectively as an end product (i.e. "in production").

That said every effort should be made in the documentation for that tool to lay bare the optimal mental model so that developers don't have to waste time "being surprised" during the on boarding process.

Collapse
 
ryansolid profile image
Ryan Carniato

There is a reality that all abstractions/models are imperfect but I dislike being reminded of it in such an obvious way. I never feel I can choose one, yet I think the most important part is choosing. And it's one of those things where certain options feel more correct to me, but I dislike the implications. And in so I end up just disliking all the options.

The truth is this example is such a small piece of the overall puzzle so it doesn't matter much. But just enough when you have to explain to someone why you made the specific choice.

Collapse
 
peerreynders profile image
peerreynders

The "why" is important and should be clearly documented so that there are no mental gaps in the puzzle.

Also more and more personalities like Miško Hevery and Stefan Judis are expressing concern about the current hyper-focus on DX.

Rust gave us the notion of "Zero Cost Abstraction". Perhaps we need more "Zero Cost DX".

Thread Thread
 
ryansolid profile image
Ryan Carniato

I guess I have some thoughts on that. I think DX is overvalued, but I think the focus on it from tool builders isn't. Honestly it is why all of this stuff exists. Even Qwik. Otherwise go vanilla over some PHP or whatever. I generally say UX over DX, but the truth is there is always some way to make better performance, some lower level you can go, but the challenge is making it accessible.

Even if the focus is too far, it's a very hard place to step back from. Almost table stakes to have that DX story. I don't see unless we hit something critical for that to change. So it won't, for now. More solutions continue to get more complicated. It's concerning to me.

Thread Thread
 
morewry profile image
Rhy Moore

I think there are steps in the right direction, though. I haven't used Solid, but IMO Svelte (despite being a variation on the theme of complex local tool stacks and compiling to rather than authoring browser compatible code) is a few degrees different in a good way, especially if you used Rollup instead of Webpack (or use SvelteKit, now). The design and results seem to make fewer extreme tradeoffs between UX and DX. There's definitely still a big ol' industry wide problem, but this next generation of things slowly gaining prominence give me some hope. 'Course, I've said that before and it's still, um, the way it is, mostly.

Thread Thread
 
ryansolid profile image
Ryan Carniato

I'm not allergic to build steps obviously, and I think they achieve things otherwise basically impossible. That new tooling might hide the complexity but it doesn't change the nature of it.

My concern about complexity is more on SSR/Hydration/Compilation side. It is exceedingly hard to achieve what we want without more advanced compilers. Solid compiles but only JSX, just like React and most historical JS frameworks. There is something nice there from the portability standpoint and that there is a clear division between JavaScript and your DSL. You can pick it up use as much or as little as you want and slice it however you want.

What seems to be coming is that we will need compilation to re-order end user code. This troubles me. Svelte, React Forget, Marko, Qwik.. maybe soon Solid.. I've been trying to see if there is another path without handing the keys over. But where I am right now it looks unlikely. So I'm going to attack it piecewise.

So it isn't about build step existing. But figuring out how we can make sense of the code we write. Is it still JavaScript? Maybe the answer is it shouldn't be and we need all encompassing DSLs but I have to ask the question.

On the positive performance is getting some attention again which is good. But it's 2 sides of the same coin.

Collapse
 
littlepoolshark profile image
sam liu

「If JavaScript surprises you then your "mental model of JS" is faulty.」
LOL

Collapse
 
ninjin profile image
Jin • Edited

I would call Vue's lazy computation approach "invisible inconsistency". What's inside doesn't really matter, as long as everything looks consistent to the outside observer.

When you access the reactive properties programmatically, the desired part of the subgraph is updated on the fly.

Yes, if you access the DOM directly, you get inconsistency in Vue. But that's a Vue-specific problem that Vue can easily solve. For example, in $mol_view, each component has two methods: dom_node that returns the current DOM element as is, and dom_tree that returns the same DOM element, but ensures all its subtree is up to date.

@mem count( next = 0 ) { return next }

@mem doubleCount() { return this.count() * 2 }

@act test() {

    this.count( this.count() + 1 )

    console.log( this.count() ) // 1
    console.log( this.doubleCount() ) // 2
    console.log( this.dom_node().textContent ) // "0"
    console.log( this.dom_tree().textContent ) // "2"

}
Enter fullscreen mode Exit fullscreen mode

The user will not see an inconsistent state either, because the whole reactive state tree will already be updated automatically by the time it is rendered. In $mol_wire, we go one step further and don't do the full update at the end of the current event handler, but postpone it until the next animation frame. Thus recalculations caused by different events for a short time (about 16ms), when the user will not see their result, are not performed unnecessarily.

The Solid model has exactly the opposite approach, resulting in low default performance, and consequently the need for additional gestures for optimization. But the worst thing is not even that, but that exceptional situations at least break consistency, and at most break the application (although the second seems to be just a critical bug):

const [count, setCount] = createSignal(0)
const doubleCount = createMemo( ()=> count()%2 ? aBug() : count()*2 )
const increment = () => setCount(count() + 1)

console.log( count(), doubleCount() ) // 0 0 OK

try { increment() } catch {}
console.log( count(), doubleCount() ) // 1 0 Inconsistent, exception expected

increment() 
console.log( count(), doubleCount() ) // 2 4 OK

// After 100 clicks outputs: 4
return (
  <button type="button" onClick={increment}>
    {doubleCount()}
  </button>
)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ryansolid profile image
Ryan Carniato

My biggest concern with it is the example where I show Vue imitating Svelte. I don't think things that should be derived should be modelled that way, but when people do that inevitably out of laziness it's awkward because part of it has updated and other parts haven't. With React or Svelte you at least know that nothing downstream has run.

Yeah that seems like a bug if it never picks up again. I think I know when I broke that. Thanks for the heads up.

Out of curiousity though what do you expect to happen around the first error case. In Vue or Solid or any reactive library count() is set by that point and doubleCount couldn't be calculated. Should it throw on read?

Collapse
 
ninjin profile image
Jin • Edited

For this reason, no separate effects are used when using $mol_wire. Instead, the same invariants are used that describe the usual reactive dependencies as well, but neither can change something on the side besides the return value. This gives a stable, predictable and manageable moment of applying side effects.


@mem count( next = 1 ) { return next }
@mem doubleCount () { return this.count() * 2 }

@mem countLog() {
    console.log( 'count', this.count() )
}

@mem doubleCountLog() {
    console.log( 'doubleCount', this.doubleCount() )
}

@mem render() {
    console.log( '[' )
    this.doubleCountLog()
    this.countLog()
    console.log( ']' )
}

@act main() {

    // initial
    this.render() // logs: [ 2 1 ]

    // change state
    this.count( 2 ) // no logs
    this.count( 3 ) // no logs

    // don't wait next frame
    this.render() // logs: 6 3

    // check memoizing
    this.render() // no logs

}
Enter fullscreen mode Exit fullscreen mode

Sandbox

$mol_wire and MobX have similar exception behavior - they are transparent to them as [if there were no intermediate memoization and the computation starts from scratch every time they are accessed] - this gives a simpler mental model.

@mem count( next = 0 ) { return next }

@mem doubleCount () {
    return this.count()%2 ? aBug() : this.count()*2
}

@act increment() {
    this.count( this.count() + 1 )
}

@act main() {

    console.log( this.count(), this.doubleCount() ) // 0 0 OK

    this.increment()
    console.log( this.count(), maybe( ()=> this.doubleCount() ) ) // 1 ReferenceError OK

    this.increment() 
    console.log( this.count(), this.doubleCount() ) // 2 4 OK

}

Enter fullscreen mode Exit fullscreen mode

Sandbox

Thread Thread
 
ryansolid profile image
Ryan Carniato

Thanks this is good information. I appreciate you taking the time to explain.

Collapse
 
eshimischi profile image
eshimischi • Edited

Dude, calm down with you $mol , habr is fed up with your mess already. When you’ll stop imposing your craft on everyone, maybe someone will find interest in it, but for now you are just an annoying hypocrite. I checked your $mol, bad technics, spaghetti code and it deffo not even comparable to Big Three Libraries/Frameworks.. stop it, please.

Collapse
 
ninjin profile image
Jin

Could you describe your complaints about $mol in more detail so that I can correct them? Where did you find the spaghetti there? Which practices seem bad to you and why?

Collapse
 
webreflection profile image
Andrea Giammarchi • Edited

I think you are comparing pears to oranges or apples here ... the Solid code does something obvious the react one cannot literally do.

// oh look, count() is a function invoke, not a constant
count() === 0; // true

// oh look, setCount(value) updates next count() invoke because
// ... that's NOT a constant ...
setCount(count() + 1);

// oh look, 2 functions invokes + a side effect happened
// synchronously hence BLOCKING the main thread
console.log(count(), doubleCount(), el.textContent); // 1, 2, 2
Enter fullscreen mode Exit fullscreen mode

And uland would do the same React does, without needing any tooling around, but that's absolutely expected.

This whole thread looks to me like this question:

Do you understand that const [value, updateValue] = useState(initialValue) makes both value and updateValue constants, not callbacks that can side-effect or have a different returned value each time, like any callback could in general, as programming rule?

As summary, I am not sure this post was super needed, fair, or relevant, as much as I appreciate all the work done behind Solid.js.

P.S. shall we talk about that blocking thread Solid.js behavior instead?


edit if anything, I think this is the only inconsistency React (and uhooks in uland) might need to solve:

setCount(count => count + 1);
setCount(count => count + 1); // results in 2 eventually
Enter fullscreen mode Exit fullscreen mode

Within the same execution stack calling setCount(count + 1) multiple times will result into having count + 1 live as next state, but using a callback might indeed produce shenanigans because the previous count might have been already updated ... however, to be honest, I've never seen a useState update function used more than once or in multiple places and within a callback ... but fair enough, this is a consistency bug, imho, not a feature, if that's the result.

Collapse
 
ryansolid profile image
Ryan Carniato

I mean React is very consistent. That wasn't really the point. It's that they are all different. That the comparison can be made because the behavior isn't necessarily a foregone conclusion for all libraries. The thing is I think about this a lot because it really is a choice. Maybe not for React, but it is for others.

And this isn't about tooling or build or anything. As you said you can do this behavior without any of that, and that's the case with Solid's HyperScript or Tagged Template Literal versions.

Someone wise once said once you need to explain something 3 times you should write it down. I've explained this one way more than that. And it's been a critical decision in a number of the frameworks I've worked on/with. So I think it is very relevant. And if you felt it singled out a single solution unfairly that wasn't the intention. This is very much a behavior/design expectation thing. With modern DSLs the expectations of what behavior should be is not very cut and dry and I think the exercise is valuable.

P.S. shall we talk about that blocking thread Solid.js behavior instead?

No idea what you are talking about. We aren't doing anything specifically awkward that I've seen, and we have means to schedule things, even concurrent rendering for those into that sort of thing.

Collapse
 
webreflection profile image
Andrea Giammarchi • Edited

beside me editing previous post to add another (fair) point ... let me answer the rest:

if you felt it singled out a single solution unfairly that wasn't the intention

all I meant to say is that if you have const [value] = thingy it's not a design decision that value will be the same for the entirety of its existence in that scope, it's simply how ECMAScript defined constants in a scope.

Saying that a constant is different from a property access (Vue) or a function invoke that "who knows what's going to return next" (Solid) is how JS works .. nothing to do with frameworks, hence my question: what were you trying to achieve here? If you wanted to explain differences you did, but it should never have been a competition, imho, as these are the basics of JavaScript, not frameworks.

No idea what you are talking about.

If you calling setCount(count() + 1) updates the DOM element textContent it means you are side effecting synchronously in the DOM world blocking the main thread ... that's what I mean.

The reason uland and React or others return 0 as textContent is because they don't synchronously update the DOM when the state changes, they schedule it, like you said before.

Now, if Solid via tools does some magic vDOM thingy there, OK, I didn't know, but if Solid.js applies state changes synchronously to the DOM it's blocking and blocking is not cool.

Thread Thread
 
webreflection profile image
Andrea Giammarchi • Edited

More explicitly, in React:

const [count, setCount] = useState(0);
setCount(count + 1);
setCount(count + 2);
setCount(count + 3);
Enter fullscreen mode Exit fullscreen mode

will result into a single update with value 3 next time ... with your examples instead, it looks like with Solid, the same operation via count() + x will result into 3 DOM updates instead, no scheduling ... that's slower, and a pattern you chose, not the best one for sure, likely the worst out of the 4 libraries or frameworks you decided to compare in here.

Thread Thread
 
ryansolid profile image
Ryan Carniato • Edited

It is a design decision for me. We have the ability to choose any API we want when designing things. I've hit this over and over. Change it to let if you feel more comfortable but it is irrelevant. This is an over simplified example to illustrate change management behavior.

It isn't a competition because as the article eludes to at the end. I'm actually back here again trying to see if I should revise my previous design decisions here. I may have opinions but this is not saying which framework is better. There is a tradeoff to all of these, and that's life.

It isn't a VDOM thing either. Inferno works like Solid in this example. It is very much a choice.

On the topic of synchronous blocking I want to challenge that a bit. I mean all DOM updates happen on the main thread so this has to be a matter of how much work you do. As I said it isn't that batching/scheduling behavior doesn't exist in Solid, more that there is a question here. Synchronously updating the DOM could be fine if you do basically no work but update the DOM. If someone wrote:

element.addEventListener("click", (e) => {
  e.target.setAttribute("data-clicked", "true")
})
Enter fullscreen mode Exit fullscreen mode

would you get on their case for synchronously updating the DOM? Solid is just a system of events that do stuff.. It runs once and wires it up and then goes and fires. You can schedule those events etc.. but that's it. At somepoint something blocks someone because the work needs to be done. Solid fine-grained just tries to keep that at a minimum always.

Thread Thread
 
ryansolid profile image
Ryan Carniato • Edited

That's the point you are allowed to have your opinion. I'm not going to tell you that ___ choice is worse for uland or whatnot. But you just did. Thank you for your input.

Thread Thread
 
webreflection profile image
Andrea Giammarchi • Edited

Synchronously updating the DOM could be fine if you do basically no work but update the DOM

that's what every framework and library does at some point in time ... but you do it without scheduling updates, and that's an issue because you might involuntarily trigger repaints and reflows while executing JS ... Solid.js here is as bad and blocking as accessing element.offsetWidth could be, because a state can cause other DOM parts to move right away so you'll have, on a complex stack, 10+ blocking things instead of maybe 1 or two, because style changes are scheduled next paint tick anyway, unless you force re-calc there, and Solid.js will never be able to batch (the topic of this post indeed) all changes at once.

You made a choice to use callbacks and you have pros and cons, but it'd be great if you could recognize the cons ... and blocking the DOM per each state change is a HUGE cons to me, and I didn't know, so I was surprised you wrote this as if Solid.js was cooler (I know you didn't mean it ... but you know, you kinda tried hard to justify that 1 2 2 and I tell you, that 1 2 2 can be a real-world performance issue in scenarios textContent is not the only thing involved in the state change).

Feel free to ignore my comment, or maybe rethink scheduling in Solid.js because this thing I've learned today is a pretty bad news to me, something I believe if properly tested in js-frameworks would push it down the slope with extreme ease.

Thread Thread
 
webreflection profile image
Andrea Giammarchi

P.S. custom elements attributes changes, as well as connection and disconnection events, are synchronous and blocking too ... if a state change an attribute and you have a reactive chain in there, for a single state change that maybe involved the whole stack you'll have a clunky experience due blocking and undesired things moved around.

Maybe for 95% of sites out there this is not a real concern, but if everyone else went for a non directly blocking approach when a state gets updated, there's probably a reason behind ... right?

Thread Thread
 
naasking profile image
Sandro Magi

it looks like with Solid, the same operation via count() + x will result into 3 DOM updates instead, no scheduling ... that's slower, and a pattern you chose, not the best one for sure, likely the worst out of the 4 libraries or frameworks you decided to compare in here.

Gotta disagree there. You're effectively saying that given a choice between correctness and performance you should choose performance, without even evaluating whether the correct solution's performance is sufficient.

JS is a wildly mutable language. The contexts in which you might be accessing state are uncountable. Solid preserves consistency of the dataflow graph when accessed from all contexts and is consistent with JS mutation semantics. Therefore it's approach is the least surprising and most flexible, able to slot in where you need it. It is clearly the right default, and should only be bypassed when profiling actually demonstrates a problem.

Thread Thread
 
webreflection profile image
Andrea Giammarchi

given a choice between correctness and performance you should choose performance

I chose correctness like I've already mentioned ... my libraries chose correctness which is the safest bet as the OP wrote.

without even evaluating whether the correct solution's performance is sufficient.

The first version of uhooks was synchronous and it's been profiled, benchmarked and tested until the point it became like React, so that all states that changed in an entire stack call would side effect with a final state once (as effect) and that was both correct, more predictable, but also enables fiber/suspense like patterns but again, effecting synchronously plays badly with Custom Elements and some other HTML or SVG attribute, but also I never talk or expose concerns by accident, too many years on reactive patterns and yes, I made my choice there.

It is clearly the right default

Not to me and apparently not for React neither, where hooks come from. Maybe solid should stop comparing its mechanism with hooks, as it's clearly too different there, for better or worse.

Collapse
 
kaleabmelkie profile image
Kaleab S. Melkie

Svelte can have consistency similar to Solid's, when (and where) you need it:

import { tick } from 'svelte'

let count = 0;

count++;
await tick()

console.log(count, doubleCount, el.textContent); // 1, 2, 2
Enter fullscreen mode Exit fullscreen mode
Collapse
 
ryansolid profile image
Ryan Carniato • Edited

Yeah. I think the key part here (and if I had added this example it would re-enforced this more), is all of these do lead to needing to be aware of this sort of behavior. React 18 I believe also has a way to flush changes early, sort of the opposite of this. Instead of wait for it, it's like force it. But similar results.

Collapse
 
sarcevicantonio profile image
Antonio Sarcevic • Edited

I think this detail is pretty important for this article, since the tutorial teaches you what tick does and why it's there.
Leaving it out and calling Svelte inconsistent feels a bit disingenuous to me.

Edit: Otherwise, I love the article! Thanks for writing it, Ryan!

Collapse
 
ryansolid profile image
Ryan Carniato • Edited

All the frameworks have a way to bypass their default behavior(React has flush, Solid batch, Vue and Svelte tick) but that isn't distinguishing, because ultimately we all end up having to choose a default. I didn't show any favoritism in that sense as I didn't focus on those as part of their description and instead focused on the behavior.

I don't have any malice in calling Svelte's default approach inconsistent, and even show some admiration in the conclusion.

Given this comment section, I understand the desire to come to the defense of your frameworks. I wasn't taking potshots, but noting this wasn't a simple question or answer. So I choose to take any comments to sincerity of my character under that light.

Thread Thread
 
sarcevicantonio profile image
Antonio Sarcevic

That's totally fair, and i wasn't aware all frameworks had a tick-like util.
I still feel it's consistent in my mental model of svelte: The variable changes because we assigned to it (like js), but reactivity (i.e. $: and template bindings) hasn't kicked in since the last round of invalidation. Use tick to force.
I guess I don't really get why it's inconsistent for you.

Collapse
 
midfield profile image
Ben Lee

This is a bit late, but I'm a bit surprised to not see the words "database" or "transaction" appear in a discussion about state consistency / reactivity. One could imagine implementing reactive components basically as SQL-style (materialized) views, and implementing state management with SQL-like transactions -- e.g. with ACID-style guarantees. I'm not the first person to think of implementing UIs in this way, see for example

cs.columbia.edu/~ewu/files/papers/...

I'm not a web developer, so I apologize if this is all old news!

Collapse
 
bradtaniguchi profile image
Brad • Edited

Is console.log the right metric to compare these frameworks with though?

The user doesn't see it, and it isn't shown in the UI, so is the entire concept of "which changes first" or "inconsistencies" lean more toward when side-effects (like printing to the console) occur rather than consistency in the UI?

This might be a bit too much of a "creating a problem to satisfy a solution" situation.

Collapse
 
ryansolid profile image
Ryan Carniato

console.log is the easiest way to show where it can be observed by a developer. There is probably only the smallest window for things to get out of sync here between the state and the UI, but for React for example this consistency is very important to them. Others to varying degree.

The interesting part I think is that these all have very similar syntax and they work a little differently. I don't think this trivial example is as important as what it shines a light on in terms of mental model and values etc...

Collapse
 
lexlohr profile image
Alex Lohr

In this case, console.log is merely used to show the behavior. In real-life applications, it's the output of this console.log example that would otherwise go into your side-effects' calls that may lead to unpredicted behavior.

Collapse
 
ivan_jrmc profile image
Ivan Jeremic • Edited

I have converted a react app (desktop like ui with apps and folders) with heavy use of drag and drop to Svelte and I ended up with corrupted wrong state everywhere, so I stayed with react. No idea if its just me or really its related to something mentioned here but the way state behaves in complex situations in Svelte just feels unsafe.

Collapse
 
svicalifornia profile image
Shawn Van Ittersum • Edited

I would point out that a lot of the surprise comes down to the semantics of the function names.

I too have seen bugs come from failure to understand that React's changes are scheduled later. I submit that the reason React's result is so surprising for many devs is that the name setCount (and it being widely documented by React and others as a setter function) strongly suggest that it will, you know, set the value of count and do it now — not after this render call returns, not a few milliseconds after that, but immediately. Otherwise how can the code have a predictable execution context? And if it doesn't set count now, then why is it referred to as a setter function, named setCount? The naming semantics are bad.

Svelte similarly fails on semantics and perhaps false promises. It sets count immediately, but then doubleCount — which is supposedly is derived from count — is not recomputed immediately and is therefore inconsistent its own namedoubleCount is no longer double the value of count.

Variable and function names are important for understanding code and communication design contracts. Any time a function or variable doesn't have the behavior or value indicated by its name is a sure sign that something is amiss, and bugs are likely to follow.

So I would say that of the above approaches, Solid is doing it right. Yes, that may sometimes involve some redundant calculations and a little less efficiency. But if the second function returned by createSignal is going be called a setter function — with a convention of being named setSomething — then it better damn well set that something immediately, each and every time that it is called. There's too many decades of precedent in this matter for a setter function to do anything else.

Wanna have a batch mode for maximal efficiency? Fine, but make it a new variant of signal — e.g. createDeferredSignal — and change the docs for that to suggest that the second function returned by it should be called updateSomething to indicate that such updates will not be immediate. Then it should be sufficiently clear that recompilation of any memos derived from that deferred signal will not be immediate either.

Collapse
 
jloa profile image
Julius

Imo the most expected flow is the one solidjs has. U write a sync code -- expect sync results. But considering the harsh reality of the slow dom and the fact that model-first concept is most comfy one, one can sacrifice the dom update consistency (as no one really cares about what dom displays at all, data-only remember) and then vue's approach seems the most predictable data-first, dom performance-oriented.

Collapse
 
3shain profile image
3Shain

I'm proposing an alternative: what if you just ban eager read


Collapse
 
guitarino profile image
Kirill Shestakov

I'm wondering whether you think this is important for a framework user, or whether it is something users don't care about?

Collapse
 
ryansolid profile image
Ryan Carniato

It's something users don't initially care about, but may bite them when things don't happen as expected.

Collapse
 
kissu profile image
Konstantin BIFERT

Really interesting as a Vue developer.
I knew that React had it's own lifecycle management state gotchas but didn't expected it to be inconsistent across all the modern JS frameworks.

Collapse
 
jesusantguerrero profile image
Jesus Guerrero

As a user of Vue for a long time I can say the approach of Vue is not a big deal for us because it is strange to need to read a reactive data from the DOM if we already have the data (I know you probably know that) but in case we would need nextTick() would do the trick for us.

Now, the 0 0 0 on react is really curious.

Collapse
 
dgavarogers profile image
Ava Rogers