Reactivity with SolidJS

Dynamic Tracking & Advanced Reactivity

Ryan Carniato

Ryan Carniato

SolidJS Creator
Reactivity with SolidJS

Check out a free preview of the full Reactivity with SolidJS course

The "Dynamic Tracking & Advanced Reactivity" Lesson is part of the full, Reactivity with SolidJS course featured in this preview video. Here's what you'd learn in this lesson:

Ryan demonstrates how side effects can dynamically track changes to signals. Conditional logic can be added, and as the condition uses reactive properties, the effect will only run when dependent signals are changed.


Transcript from the "Dynamic Tracking & Advanced Reactivity" Lesson

>> I wanna emphasize there is some more power in this, I was focusing a lot on the lack of work you do. But the way we work, we actually do cleanup, is the best way I can explain it, and this ensures that only the current dependencies are tracked.

And this is something that you actually can't do with the compiler, and this is why we rely on a runtime reactive system with Solid. I think it's just easier to show than really explain this, so I'm gonna open up, yet again, one more example and kind of walk through this a little bit.

Cuz we're still playing with our first name and last name. And this time, I've added one more signal, where we wanna toggle between showing the first name or showing the full name, right? And what I've done is I've kind of changed our memo now, so that if you're not showing the full name, return the first name.

And otherwise, do the full thing like we're doing before. And again, we've got our effect, which now does our display name. And what I wanna show here is that, when we create, it's kind of the same as before. We're just gonna log our John Smith here. Similarly, when we set show full name to false, we're just gonna see John, cuz this is gonna rerun again.

It's gonna realize that it's false and return the first name. But where things get interesting is, what happens if you change the last name? Well, you're only showing the first name. And for this, actually, you know what, I'm gonna just put an extra log in here, just so that we can actually see what's going on.

We'll call this one "Create/Update displayName". So let's look at this again, Create, Create/Update dsplayName. My name is John Smith, set the full name to false. We obviously have to update the display name and set the name to John. But now, look, when we change the last name, nothing runs.

And the reason for this is, the last time that this displayName ran, it only listened to first name, it did not listen to first name and last name. So the runtime reactivity is smart enough to know that it's not listening to last name. And you might be like, does this mean things can get out of sync?

It doesn't though, because it's only possible for this to rerun again if this condition around the showFullNName actually changes, or first name changes. And the first name changes, we don't care, we're just gonna run this again and it's gonna shortcut to here and show the first name again.

However, if I set it to full name again, it's gonna rerun the function, at which point it's gonna grab the latest value anyways. And we can see that it did, in fact, change it to legend behind the scene here. And at that point, it reruns the function and updates to legend.

This is an important behavior to understand, because if there is a decision point in your code, it has to be also reactive. Because if we just went in here and didn't use a signal to make the switch, it's never going to retrigger to go. So if I just made showFullName or something like, if I just made it something like, I don't know, let showFullName = true initially, I think.

And then I get rid of this function, and then instead of calling a signal, I just was like, let's do something here. showFullName = false, showFullName = true. What you're gonna see is that, It's not gonna be smart enough, to be able to determine what has changed. Essentially, when I go set full name to false, nothing happens.

Change name, it actually still tracking the full name, so it actually reruns again. At that point, it realizes that it only needs to show the first name, and then when I set it to false, [LAUGH] then it actually is still only showing the first name. So I just wanna bring this up front cuz this is one of those kinda like you have to be aware of things.

It's incredibly powerful because it optimizes your code flow, but it means that you are entering a world in which things are signals and things are reactivity. Reactivity is something you can lose essentially. So this is why this is a key concept.
>> The biggest thing is that all has to be a function call-

>> Yeah,
>> Of signal or whatever in order for the dependency tracking to be correct.
>> Yeah, it's gonna be one and the other primitives everything's kind of derived from this basic language. You can build that up as much as you want, but at its core, we have these signals and we have these tracking things like effects.

And basically for anything to change, it has to be within that system. Reactive libraries were actually kind of popular in the early 2010s. And they almost died out immediately with the dawn of React. So I wanna point out this isn't a new idea. And this is why I'm kinda talking a bit about the trade-offs to understanding that, cuz I think it's important.

One of the biggest reasons so, is that the early reactive libraries lack predictable execution. You might in your head already kind of be picturing, what if I have a whole bunch of stuff that depends on a whole bunch of other stuff. And then I'm ping-ponging it around and making dependencies or write other dependencies and making a huge mess.

And I think this is a fair consideration. It's why with all JS, for example, we're very careful about things like two way binding. We've learned a lot from React in terms of things like unidirectional flow and immutability. Because adding structure on top is actually fairly important, cuz with this amount of power, things can go pretty crazy.

But the second and really key safeguard here is that React systems like SOLID or MobX actually look at your graph at runtime and topologically sort the dependencies in such a way that they can guarantee that on any change, every function only runs once. And for that reason, you don't have to worry, unless you're doing stuff purposely, maybe not purposely, but trying to push stuff around in a kind of very chaotic manner.

You can depend on stuff executing in a certain order. So that has changed from the early days, and I think that's kind of important to kinda point out at this point. We'll be covering this a bit later on, but there is actual some APIs here that are more advanced to kind of handle these kinda situations to avoid things like untrack.

The ability to say, hey, I don't want to listen to these variables under this scope. It's kind of the inverse of something like React's dependency arrays. In React, you have to always provide the dependency arrays. Things like untrack are saying, don't listen to this. The default is that things are kind of memorized and that they automatically update.

This is how you say don't update. And batch, I'm mentioning here, it doesn't come too often, because internally, everything is batched, and so it all kind of happens at once. But sometimes when you're integrating with third party systems, you wanna apply many changes at the same time instead of doing them all independently, and batch gives us a way of doing that.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now