In this video, we learn how to use computed properties to create derived values in models.
There properties automatically update across models, and in the UI, how to use JavaScript getters to create efficient computed properties. How to use computed model properties in a UI and see how they can improve performance with targeted component rendering.
00:00:09.155 Let’s take a moment to talk about observability and how these models can be used to reflect changes in the UI in an efficient way.
00:00:20.445 So to do this, we’re actually going to create a new component that will help to demonstrate this behavior. But let’s take a look at the relationships that we have between our models and how we want things to update.
00:00:35.435 Let’s say we had some data on the system info that might be changing on a semi-regular basis.
00:00:42.285 And I want to make that data and combine it with some data on the dependency model to present something unique in the UI.
00:00:53.595 I don’t want to have to continually be listening or polling or anything for changes, I want to be able to rely on the fact that these models are inherently observable.
00:01:03.665 What that means is that any changes that they make to any of the properties are automatically propagated to anyone who’s expressed interest in one of those properties.
00:01:16.305 That could be another model that says, I’m interested in this piece of data, or it could be a UI component.
00:01:25.575 And the way you express interest in the data is really just by using it.
00:01:30.215 That’s one of the nice things about the model framework to begin with, and one of the more powerful pieces of it.
00:01:37.315 Just by saying that I care about this particular field is enough to say that it will be notified whenever this field changes.
00:01:47.135 It’s the same in the UI.
And this allows us to have extremely targeted updates that will reduce page refreshes and generally keep things running a little bit more efficiently.
So the framework is able to keep all of these dependencies in mind so that the user doesn’t need to worry about cleaning up after them and making sure that they’re used efficiently.
So let’s show this in a real sense.
Let’s go back to our system info model, and we’re going to expose something nonsensical at this point, but it’ll still be valuable.
So we’ll go back to our info.
We’re going to create a counter here.
Let’s just call it the date in milliseconds.
That’s going to be a number.
And so in here, we’re going to create a implementation form, which is going to have a date in milliseconds, which is a number.
And we’ll instantiate it here as with date now.
So that’s now part of the interface.
And I can go back into my UI and show that particular value.
It’s going through the dispenser into the system info, and I can get the date in milliseconds.
When I refresh the page, it comes with the date in milliseconds.
So that’s in itself, nothing we haven’t done before, but let’s go change things up a little bit.
So let’s go back into the system model and rely on one of the lifecycle methods to go and change some of the data intermittently.
So let’s use the init method.
So this will set it up reasonably early in the lifecycle.
We’re going to do something that’s just going to say setInterval, and in this setInterval, we’re going to update the date in milliseconds.
So when we save this, you’ll see that every second, this particular field is updating.
So I haven’t had to do anything to listen for those changes.
Just by virtue of me using that field, the UI is updating automatically.
Now is probably a good time to talk about some of the rules of data updates and how we want things to work.
This is fine what you’re seeing here, where it’s wrapped in a function and where we’re updating the value, but generally speaking, you need data updates to occur in what’s called an action.
You can think of it like a transactional boundary, and so there are some cases, especially when you’re doing asynchronous work, so if I had an await here where I was going to pull things in, the rules of the closures tend to get a little muddied, and so by putting things into their own functions, and so you saw me do this up here where I put this operation into a separate method, the framework is able to keep track of the updates a lot more cleanly.
So rather than doing a bunch of updates at the same place that you’re doing the asynchronous work, by delegating it to a helper function or a method, you’ll find that the updates are a lot more clean, and it also reduces the amount of churn in your UI.
So there are a couple of ways you can do that.
One says, you know, wrap it in a function like that, create a method, but there’s also a utility method that exists from the KOS framework just called KOS action, and KOS action accepts an empty interface, and you can perform all of your data updates as needed in here.
You don’t need to use this all the time.
As I said, in most cases, if you’ve got a separate function where updates are happening, you can avoid that.
So if you find yourself using the KOS action a lot, it’s probably worth asking whether it’s necessary, but there are going to be some cases where you want to make sure that things are encapsulated properly.
So KOS action becomes a tool by which you can guarantee that the boundaries are being respected and that things are being updated in the right way.
So I’m going to slow down the timer a little bit, just so it’s not quite so busy, and we’re going to start to use the fact that this data is changing on a semi-regular basis to show another concept, which is how we can actually compose the data in another model.
So we’d kind of talked about how this is updating, we can see it reflecting, but we’re drilling down into the system info to make use of it.
Now, let’s say we actually had something up in the dispenser model that relied on that data, but we wanted to make sure that it was updating at the same time.
All right, so we have our dispenser model here, and the system info is being used.
Now let’s say we wanted to create something that combined the dispenser name, which is dispenser with the date and time, we wanted to make sure that it was combined.
So to do that, again, we’ll go back to our contract that says we have an interface, and there’s going to be a dispenser name piece, which is going to combine those pieces of data, so name and date.
And it’s going to be composed from a couple of different data sources.
So go back into our dispenser model, we need to provide an implementation for that.
But rather than having to go and create a property for that, we can use a getter.
This is, again, one of the conventions that exist in the system.
This is creating what’s called a computed property.
So by using a getter, there’s no setter, so I can’t change the value, it’s not mutatable.
But I can say it’s going to be a result of returning this name, as well as the system date in milliseconds.
Or maybe we want to do something a little bit more unique as well, which is let’s create an international date time formatter.
We’re going to use this data to kind of drive a richer date.
And we’ll format it with that value.
So now we’re going to have the name concatenated with a formatted date.
So this is a computed value that I can now use in my dispenser, or in my UI.
So if I go in here, and I no longer want to drill into the system info, and instead I’ll use the dispenser name and date, when I refresh the screen, you can see that it’s now concatenated these things.
Now, I’ve used a date time format that’s not terribly interesting, because it’s not updating all the time.
But it’s important, because if the string value doesn’t change, it’s not going to cause the UI to change.
So even though it might be updating in the model layer, so even though in here, the dispenser is actually receiving updates every three seconds, because the value of the string remains the same, the framework is really caching that value, and only propagating it to interested parties when the value changes.
What that means is that this UI component is only going to update once, whenever the date changes, not every single time that it’s receiving an update.
That’s really important, in that it’s a way of ensuring that you can have extremely targeted updates to only those parts that are interested in data changes at that point.
So let’s maybe take this a little bit further.
I believe we can put some options in here.
And that’ll allow us to specify how we want to format the data.
So we’ll say the time style is full.
Now when I refresh it, we’re back to getting updates every three seconds.
So the fact that I can use these computed values, and this becomes incredibly important as you start creating models that are composed of other models and using data.
It doesn’t need to be just strings, it can be entire objects that are being created, and they’re only going to be notified when there are subsequent changes to other models.
This becomes incredibly important in terms of how you think about your models and how they get tied together.
So this is all well and good.
We’re doing everything all on one component, and there is an interesting thing I can show you where if I pull in a Chrome browser which has a slightly different set of tools, you can see that every three seconds you’re getting paint flashing.
So I’ve gone into the DevTools and I’ve turned on paint flashing here to show when the components are reloading.
So you can see that it’s actually updating pretty frequently because this is changing pretty frequently, but it’s reloading the entire component.
It doesn’t need to be that way though.
I can control that by creating component boundaries that are going to allow me to only have the pieces that I want to refresh at the one time.
So we talked about before being able to create a new component that’s going to just encapsulate the system information.
So let’s go and do that, and this will be our first time generating something using the tooling.
So we talked about the development tooling that exists in the framework, and one of the things that exists is a code generator where I can say I want to generate a new UI component, and I can say what do I want to call it, and where does it exist.
In this case, it’s going to go into our dispenser UI, and the generator will say I’m going to create these files here.
So it becomes very easy and quick to create new components using a structure that works well with the framework.
So let’s go back up to where we said we wanted to create it.
There’s our system info view, and it’s relatively simple, but there are a couple of key points that you want to see.
First is the component itself, it’s a React component, and it’s wrapped in this KOS component, higher order component.
And this is really where the magic happens from connecting the UI to the model.
By declaring this as a KOS component, it’s now going to be listening for any changes to any of the models that are in there.
And so any properties that are being used that might change as a result of a mutation in the model will cause this component to refresh, but it’ll only care about the fields that it is interested in.
So what does that mean?
Well, if we take our component and we say, I’m going to change this a little bit, and I’m just going to take a system info, which is a system info model as a property, and I’m going to pass that in.
And in here, let’s take what we had before and render it based on the data that’s coming into, based on the prop.
So I no longer need to, I’m going to take the system info and pass it in as a property here, and it’s going to be able to render it.
Now, we had another piece there, which is the date, which is the date in milliseconds.
That was one that was changing on a regular basis.
So now we have these two pieces of information.
I have a system info, and this component is only going to re-render if either the app class or the date in milliseconds changes.
Right?
So we’re going to use this component in here, which is our system info view, and it takes one parameter, one property, which is going to be the system info.
So same thing, we’re now getting this refreshing, and if we pull in Chrome again, you can see that now only this one field is updating, so we’re getting a very targeted update.
So even though other things might be changing in other places, or other fields might be listened to, that component is only going to refresh if one of the fields that it’s consuming ever changes.
So this comes down to how we do our component design and our models.
You can think of a model as representing an observable boundary, and it will allow us to create very targeted updates, and if we pass our models into our components, we can create these experiences that are going to be more efficient and easier to manage.
Thank you.