Let's fix a small UI glitch that was occurring on startup and whenever the refresh button was clicked. In this lesson we will see how to avoid common bad habits of imperative programming, and how to use reactive programming to keep the complete dynamic behavior of a value specified in one convenient place.
[00:00] In our user interface over here our refresh button actually has a small bug, which might annoy some users. When we click refresh, the current list should be immediately cleared away. While it's loading, it should stay empty until new data arrives.
[00:18] Actually what happens is that as soon as we click refresh like this it doesn't get cleared immediately, but it stays as the old data until the new data arrives. This also happens when we loaded the page initially.
[00:33] Before the first response arrived, there was actually a flash of undesired content, which was this placeholder in each of these suggested users. What we need to do instead is first clear the element on startup to not have any text. Second, we need to clear the element again whenever we press the refresh button.
[01:01] I just changed here the render suggestion function such that you can give a null as the suggested user data. If it's null, then it will simply hide the element over here. What you're probably thinking right now is that we just need to call render suggestion "Null" and "Suggestion one." It will clear the first suggested user's element here on startup.
[01:33] Then we just need to do this again for the second and the third, and then there we have it. On startup, all of these elements will be cleared. There will be no flash of placeholders, but not so fast. I know this would actually solve the problem, but let's reflect for a moment. Why is this a bad idea?
[01:52] The element over here is a dynamic value changing over time. In lesson number three we learned that in reactive programming we should specify the dynamic behavior of this value completely at the time of declaration.
[02:09] Where was this declared? Well, if you think of the data that is behind this thing, this was declared here in suggestion one stream. Where was it actually defined? Was inside this create suggestion stream, if you look at this, you cannot see that at startup it is empty.
[02:31] You simply don't see that here because that is not specified here at the time of declaration. We need to somehow move this behavior of change. This is somehow specifying a change. We need to move this change into the declaration of suggestion one stream, all right?
[02:51] I'm going to do that with a nice operator called "Startwith." You're probably thinking, "OK, what does that do?" It is a pretty simple operator. It does the following. Given a stream of some sort, and let's say that this response stream happens some time later in time. It happens not immediately, but it happens after a while.
[03:21] This is mapped to a suggested user as we saw here in maps for a random user. This part near here is this stream that we previously had. What startwith does is basically it just prepends that stream with a value that you specify. It prepends it at the beginning here with a null and then later we have a user.
[03:47] That's what startwith does. That's how we can clear the element at startup because this is the first initial moment, and that's our startup moment. That's what startwith null does. What about the other feature? On refresh, we should clear the element.
[04:09] Well, same story. The dynamic behavior of this element should be completely specified at the time of the declaration. What is the time of declaration again? It's over here. Again, we're not trying to change this when refresh happens.
[04:27] One naïve approach would be to take this refresh click stream, refresh click stream and we subscribe to it. It's an event, and then we simply go and call render suggestion with null for each of those suggestions. Again, this would be something else changing that element, and the dynamic behavior of this element would not be defined here at the time of declaration.
[05:01] How can we include this behavior here into the time of declaration of the suggestion stream? We can do something like this. We can merge with the refresh click stream, and we're going to map each of these refresh click stream to null values. Even though there's a refresh event there we're just going to map that to null.
[05:31] If I make this code look a bit nicer, yeah. Now we have the startwith there, and we're going to merge that with, let's say, over here happened refresh. That refresh will be mapped to a null. It will look like this. Let's say that this was, also refresh. We're going to map that to a null.
[05:54] Then when we merged these two folks, we will see something that looks like this. It starts with a null. Then it gets an actual user data. When you refresh, it got a null again. Now let's say you clicked refresh again, and some time later that data from the response will come here. Let's actually write it like this. We're going to have our user there.
[06:19] Now when we click refresh it will immediately clear this list. Then after the loading passes, it will show us new users like that.
Hi Roaders, we wanted to introduce only one new concept per lesson.
Great, thanks for the reply.
Would you be getting a race condition between getting the response from the requestStream and the null event from the refresh stream?
Hi Michel, no we wouldn't, because from refresh to the null
emission, all of this was synchronous, but a response is asynchronous. In worse cases where both are asynchronous, we can still neatly handle race conditions in RxJS using either concat or concatMap or zip, depending on the situation.
This part of transcript is false, else condition must be : 'suggestionEl.style.visibility = 'visible'; if (suggestedUser === null) { suggestedEl.style.visibility = 'hidden'; } else { suggestionEl.style.visibility = 'hidden'; }
That is not an advantage for me. I still wanna show the data, until the data has been completely downloaded from httpRequest and then shows it (directly switched over to the new avatar and text) so it won't be any cleared or spinning loading animations. How could I do that ?
Hi. Great videos, many thanks. Why can't you use start with in the previous lesson to load the initial list of users before the refresh button is pressed rather than merging the 2 streams?