1. Functional Requirements and States
When fetching data from a backend, applications must handle loading and error states in addition to the data itself to provide a stable and predictable user experience.
Watch section from 0:00 →Engineering video
22:01 · 3 October 2024
When fetching data from a backend, applications must handle loading and error states in addition to the data itself to provide a stable and predictable user experience.
Watch section from 0:00 →Applying the Single Responsibility Principle by splitting the dropdown into a logic wrapper and a stateless presenter component improves readability, maintainability, and testability.
Watch section from 9:55 →Using native semantic tags ensures the DOM is accessible out of the box, helping avoid the anti-pattern of maintaining excessive ARIA attributes on non-semantic wrappers.
Watch section from 14:00 →Rendering content fetched from servers requires considering sanitization to prevent potential XSS attacks if user-persisted data or unverified inputs are ever rendered.
Watch section from 14:48 →Using React.memo on the presenter component ensures it only re-renders when its props actually change, optimizing browser performance.
Watch section from 15:56 →Hi there folks. So let's jump right in. What we will do today is to look at a component that somebody else, in this case it's me, and this component has what I call junior level code in React and we will factor this to get it all the way to senior level. And along the way we will see how a senior thinks and what are the guiding principle a senior developer would use to kind of solve this problem. So you can incorporate this in your code. Let's get going. Um, let's first overlook what we are building. So basically what we're building, it's a drop down component. It has some options and a selected value. The options get fetch over the network. So basically, we get those options using a function that mimics a network behavior and then we basically render them and we render an option for each one of them. So, this is a basic select element and whenever the user clicks on it, we actually update this state, which is the selected value. Let's see how this looks like. Let me run this. If I'm not running it already, I might have an open tab. Perfect, it's already running. And basically, this is how it looks like. Right now, the component is loading. And I think yes, perfect. And these are the options that came through. Whenever I select one of those, it gets selected. That's pretty much it. Now, this is a junior level implementation. It does satisfy most of our functional requirements, but not all of them. And we will start there. So, always keep this in mind. Whenever you don't know how to start in interviews and so on, we always start with the functional, functional requirements. Which is basically, hey, what do I need in order for this to work? What does the user needs to interact with? So, functional requirements. Now, on the surface, it looks like this, the drop down satisfies all of those. But in reality, there is a big flaw here, which is whenever we synchronize state with the backend, or as civilians like to call it, fetching data. Whenever we get something from the backend, like in this case, we get the options, we have two extra states that are not the data. And those states, as you're probably thinking, are loading and error. And this is always the same. So keep this in mind, if you fetch data, you need the loading and error state. Most libraries these days like React Query, give
is by default. In this case, it's not there. So let's add the loading and error state, right? So, first of all, we're going to have a very simple one, which is a Boolean. Uh, whenever you name a Boolean, we use is or has as a prefix. Um, so it's loading and they said it's loading. I'm not using compiler, by the way. This is a human writing code, try to write code without compiler when you're not um when you don't have too many deadlines so you're actually practicing.
So, um we're going to use your state and we're going to initiate that as false. initial initial condition of loading, it's false. So now, let's see what our what are our state transitions. So, we want to start this loading when we start fetching. So I'm going to say here, you know what? We're to set it's loading to true because we are loading something. And whenever we finished, we're going to set it's loading to false.
Pretty self-explanatory. Now, although we do update the state when we fetch and we finish fetching, we're not using it. So let's go ahead and also use it as a functional requirement. As a user, we probably do not we don't want the user to interact with the select when it's being when things are being loaded. So I'm just going to say, you know what, um we're going to have disabled, like this is a native HTML attribute and if it's loading then let it be disabled.
The other thing is we might want to display a value of this as, hey, we are actually loading. So what we can do is to say, you know what, if it's not loading, so only render the options if you're loading, so if you're loading, do not render the options. So now, it's loading. There's no options if it's loading. And we can actually display a high coded option if it's loading, so say is loading, then uh display right in option, option and his options will be loading.
So, it will kind of display a loading option. Let's see how this looks like. Perfect, there we go. Now, this looks a lot better. We have loading, it's disabled. And as soon as the options come through, you will see. Okay, so functional component functional requirements first. They go first, they're not obvious, they're not values. We've seen a lot of people failing interviews because they don't pay attention to those.
Number two, we need an error state. We're fetching things over http, there might be an error happening. And before I do that, I'm going to fake an error. Let's fake an error. We go back to the function that I implement here, which is a mock fetch, right? It's just a fake promise that I'm making and I'm resolving that in 6,000 seconds. I'm going to decrease that so we have faster feedback. And what I would say is, you know what, I want this to randomly fail. So how can we do this? Well, we basically can say generate a number with math. random, and this will be a random number between zero and one, and if that's higher than 0.5, then reject the promise. Do not bother resolving the promise. And reject it with an error as secure. So pretty basic um random error. Let's see if this works. So again, it's very handy for you to know how to modify promises. Even if you are a developer, you need to know very well JavaScript. Now let's reload this a bunch of times. Let me save that file, let me see if I save and save and I'm saying, okay, if mad, the random, it's higher than 0.5, then we check that. And perfect, and else then resolve it in a certain time, okay? So, if I refresh this a bunch of times, I would definitely expect this to fail. Let's see if there's any trailer. Okay, but my component doesn't really reflect that trailer. As you see, it stays either it stays in, you know, it doesn't really it just stays that and then it stays as loading. So this is very misleading as a user. Very important, the state of the application should be somehow reflected to the user. So, let's add our error state also. part of our functional requirements. And for error, I'm going to simplify this. I'm going to uh just have its error, but normally we want to know more than just a boolean about our uh about our error. So we could say um it's error, actually I'm going to call this error in, I'm going to set set error in, and I'm going to start with use state where our error is just false or it can be a string, and going to define the type of this, I'm going to say this could be either a boolean or a string. Now, we do have our error and so let's wrap all this. I'm going to why why do you pop up? Perfect. So, let's wrap all these in a try catch. I'm going to use my editor there, it knows how to deploy this try catch and oh my God. I'm doing this with a notepad on my Mac and so it's super annoying, but okidoki. And so we we now cache the error and we want to set the error to, let's say, plainly the
you know, like we get there, I know this isn't an error object. So let's see if I can get the error message and for some reason they're telling me that yes, perfect. So the the type it's unknown, um, we could make that work with different types, but for now, I'll just say an error occurred, perfect. A very robust way to display an error, okay? Now, what do we do if there is an error? Well, instead of actually displaying, loading state. We could actually modify this code, but that will lead to very convoluted JSX. So this is the next senior tip, to be more deterministic, we'll actually write our alternative paths, alternative paths separated, okay? So, I'm not going to modify the JSX here, which would be like, oh, is there an error here or something else, but actually take it out here and say, you know what, if if there's an error, then we're going to do something else. Now, I do want to maintain the integrity, the visual integrity of this. So, I want this to uh look as close to what it looked when the happy path happened. And that's why I'll just copy this and say, you know what, I'm still going to display a select, it will be it will be um basically disabled. So I'm going to say this is two. And I will show as an option, um as an option I would always show it, show this option over here and say, you know, this is my error message. So, I'm going to display our error message. Okay, let me format the code. Perfect. So, if there's an error, uh show pretty much the same. I'm going to take this off. I don't need all that. Show the same select, but it's now disabled and display that error. So this it's a stable UI and I will do the same for the loading for the most part. I should do the same for the loading and you'll see in a second why this is a lot better, I could say if this is loading, then display this and show here loading. Usually, these texts should come from a translation kind of sort. So, and all of a sudden, I do not need this logic anymore. So, you see this will make my GSX a lot simpler, a lot easier to read. So, let me format a document and this is another lesson, okay? We always want to add our conditional logic like this. But we do keep the same structure in order to keep layout integrity and not to have layout switch when any of the scenarios happens. Let's see how this looks like. So we're basically loading. Uh okay dokey.
something went wrong. Let's check our console. Everything looks okay, but it's definitely not okay. Let me see what's happening here. We say if there's an error, and show that there's an error and show the error. Let me see, we said yo tonight, so we should have yo over there. It's disabled. Let me reload this. Maybe oky doky, I don't have any visibility on that they're happening. So I'm going to go ahead and also log something to console that error say and error occured.
And this will help the developers kind of see what's going on. So let's refresh that. They are cured. And now it kind of compute, let's see, we do have this kind of work. It works. Let me clean this up. Refresh. Okay, so it looks like we are always displaying this. Let me give you this and add an else so we make sure that if the promise rejects, we do not resolve afterwards. We kind of had both scenarios at the same time.
There you go. Now what I'm missing is that loading indicator. So it does load, but all of a sudden it wasn't displaying my loading text. Because I made a new by mistake, which is that there's no return statement over here. We want to return. There we go. Now that's the behavior we wanted. It's stable layout. Okay, in this error, we have an error. Not very pretty. We could work with an ellipsis property here to make sure this text doesn't overflow and we have stability on the X-axis too.
Take note of that. Now we do have our layout stability, we finished our functional requirements. The next thing would be to look a bit at the code organization and finally I will look at non-functional requires. Now see, as a senior developer, you should be able to talk about this code, however. And this is one of the lessons I want you to take out of this video. Is that seniors can drive implementations.
They're not passive. They do not need time some. They do not need someone to tell them how this has to be built. They have their own standards on how things should be delivered. They can adapt to the situation, but if we have enough time, they could go on and talk about five hours. About the senior that you're going to need. seen a second that we can actually do that. I'm not going to spend five hours in this video, but I want you to know that it is possible.
That we can go very deep into this. So in terms of, in terms of code organization, um, it's pretty obvious right now. there there's two parts to this component. There's one of them that's just kind of painting everything, which is this bottom part, the JSX part. Um kind of only presenting it. And then there's the logic and we could split this into a custom hook and then we have a one liner. But I like the idea of stateless components.
So I like the idea of breaking this apart. And I'm going to apply the solid principle here. Solid, the first one, which is a single responsibility principle. And with solid, what we will do is that we split basically our drop down into a drop down wrapper. Let's call it that way. That will handle all this logic and then we take all this, which is our presenter and this will be our presenter component and it has no lot.
So I'm going to start here and I say that this will be my return presenter drop down. Let's call it that way. And so this will receive all those props. Now, let me declare that, and because this is hoisted, I can do it even here. Presenter drop down and basically this will receive some props, which we'll see in a second which they are. And it will then finally return. Well, basically what we were already returning it.
So I see there's loading error. There's um on change and selected value, so let me come let me declare an interface. Um as the presenter drop down. I do not have to help me out. Oh my god, it usually would be able to really print this out in a second. Well, this is good practice, this is good to see that we can do things by hand too. This loading would be a boolean and then we have our options. Which are an array of option and then what else do we have?
We do have our on change on for now, I'll choose this very uh robust on change. Which is like a function that returns void or we can type the argument later on. And so this would be of the type presented drop down, have our error coming to which could be string or a no. And then we will have our it's loading coming too. We're going to have options and we're going to have our on change. Is there any other error here dates selected value?
And I think in our case, selected value it's actually string. And so we also have that one. Pretty straightforward, right? I take all my presentation layer and I am added here. And now we will pass all of them um in line so we make this pass. Also, as you see, um this is one of those cases where type script actually helps. It's not getting it away. Uh some days sometimes um we need to make all those type gimmicks as they call them. Um to to string, okay? String to make type skip pass, but it's not the case. We also have our on change uh on change. And finally finally uh to to perfect. You see, so this is an event of change event that I need to type out. I mentioned that. I didn't type out the input and finally, you see I'm using perfect. And then we have the selected um selected value. Let me just edit here. Selected value, it's actually selected value. So, perfect. Now, this is now our component. It looks a lot cleaner and I could actually split files. I'm going to do this with the editor. Uh, why is the dictionary opening all the time? Anyways, I'm going to select all this code and I'm going to show you a little trick that I do love, which is I take all this and I'm going to refactor and I'm going to move to new file and usually it works, but when I have learned the recording software it usually doesn't. Um normally it should move all this to a new file out of the box straight on your own device. Now, we do have our um solid principle applied. I split this, looks a lot cleaner. I can worry about the logic here, I can worry about anything else here. It's a lot easier to test this now. I can mock everything, you know like unit test. And it's also a lot easier to test this in isolation because honestly, um all the logic, it's kind of leaving here. So that was that. We we applied solid principles to refactor the code. And now there's the non-functional requirements, which is the last part I want to talk about. And usually in the front end, we will talk about performance, as in web performance, not scalability. Web performance is basically how the application performs in the browser of the user. And we will talk about accessibility. Accessibility. Okay. I always misspell this. What's the problem here? React accessibility. There you go. Now, these two non-functional requirements, um there's another one that it's web security. We'll see in a second that there are some nuances there, but nothing not too much. So let's
look uh number one accessibility. Um when it comes to rendering, there is most of the logic it's in the presenter they up down. And that's cool, because this is the only one that has now to worry about accessible. Um, the best way to make such something accessible is to use semantic tags. Div is not the selected uh semantic tag. We're not going to using it, so do not add necessary divs to your components.
They do not help, so I'm going to take them out right now. Other than that, all the code here it's semantic. Which means the accessibility doom that is being rendered knows exactly what's going on here. I do not need to add area labels and remember area attributes, which is what we need to make non semantic HTML accessible. Area anti pattern because they need to be maintained, so you want to avoid them as much as possible.
Try at the very best to write semantic code. This is semantic HTML, so we can move on with accessibility for now. But we'll see later the case where this becomes important more later on. Let's talk about web security. Now, the only problem that can happen here is because we use this label and this comes here, they could send like an attacker could possibly send HTML or JavaScript here and we might somehow end up rendering that. Um it's a variable, but that that's the only thing. So you might have to make some checks on the sanity of this option list. So whenever we fetch the option list, we can check that there's no HTML tags or JavaScript, like script tags inside the text, and maybe we can check a max length. Other than that, but um assuming this comes from our server, um we could probably safely move on. But this is what I would mention on web security, sanitization, because we render content from server. And so uh there's different attacks that an attacker could um could basically uh perform with that. Um uh yeah. But for now, um let's assume that we don't need to sanitize these values because they are from our server. They're not user persisted.
If there's the up down that the up down would come from something a user would add, like imagine it's the up down usernames, um a user might add in their name like an HTML script that could end up running, um basically doing an XSS attack on the uh on on our user's website without them knowing. Finally, performance, and this would be react optimization. Basically here I would look at re render and you know re render.
Um we could maybe run and use callback over here, but it's not very useful. And then whenever we return this like this presenter, the dropdown, if it would have some props, the best optimization is to run this uh to wrap this in memo. So basically, instead of returning the drop down as it is, I will take it and then use memo on it. Um again, this doesn't receive props, but here we do receive a lot of props.
So this is where uh the presenter the drop down could actually be optimized. So I'm going to take all of it, I'm going to cut it out and use memo and pass it as a argument to memo. And what memo does is that it will check the um it will check the props. So it will make sure that this only we renders when the props actually change. So that's the first optimization when it comes to performance, I will check for the keys.
They are unique um and um yeah, that's pretty much it. There's no need right now to do anything else in the performance side, I do not see any opportunity for use callback or use memo. There's no expensive values here. But do keep this in mind. So two hooks. Um so that was optimization, we look at accessibility, and now finally, a very advanced, kind of legendary level. Let's say legendary React level would be, can we build this without the select select slash option? And I would estimate this takes around 30 minutes.
So I'm not going to go into it, but I'll give you a rough um idea of it. But basically you want to do this when you want to have your own own styles. So if having our own styles in the dropdown, it's a requirement styles. Then you need either a library that gives you the dropdown, or you need to build your own dropdown without this select. And that's when the challenge comes in because all of a sudden there's a lot of behaviors here.
That we would need state for and we'll also need to style it. And we'll also need to make it accessible because it will no longer be semantic HTML. Okay? That's one mistake, so uh probably in this option, I could add an area error kind of uh the scenario error message. Yeah, and and then we put the error here, error, and probably now it will tell me to tip by the roll option. Perfect. and something along those lines, if I want to make this to be like an error, if I will get the same because I cannot put the span inside a select. So I'm going to make this a div and I would need to style them. If I want to be 100% accessible, so why is this so we have a div and then span the property okay, so basically error message, if there is an error, it's basically telling me that this can be a string only fine, so error shouldn't be I can only use error because because error it's of type. So if error let's an error. linked so it's greater than zero, uh no, then I could say if the type of error that's equal equal to string. Okay, kind of working around. So, but right now this would be more accessible so we can overlook that. And if it's safe for loading, I could actually I should have to replace that and then I need to style these things, and then I need to point that this is a container. Um, now going back to rendering this as a non-native element. That would take a lot of work. But what we would need to do is to actually render this as a button and then render these options as a list and then style them accordingly. So I'm going to edit here. This should be a button, this should be a list element and I will need state. I'll need state for every, if you click on the button, we want to show the option list. So all of a sudden this stateless component wouldn't be stateless anymore. It would need an its open state at the very least. And now, if we look at it, um let's refresh. Okay, it's loading now. You see, when I click, that's the first state change that happens. So I need state for that. And when I move, I'm also selecting it, right? So I'm selecting a different one and that will be the second list or we can call this. Highlighted option. So it'll be my second state variable. Okay, so I'm going to have an its open state and then I'm going to have a highlight highlight element state. Right, these two are the ones necessary to replicate this behavior. And what am I going to have as state transitions? Well, whenever I click, I change, right? When I click click here, and click outside, I need to close that. So either click on here and I close, it's open, becomes closed or I click outside. So I'm going to need a ref. That's another thing I'm going to need. I'm going to need a ref to track clicks outside. So all of a sudden this will become a very complex.
ponent. I would not do it in this section, I might make a super advanced kind of legendary, um, example on how to refactor it. But the main idea I want you to take out of this is that whenever you are refactoring, a senior level candidate could probably go in and um, go on for three hours, right? They will never run out of things to say. Because one of the things we didn't talk about, it's also testing.
So you could go ahead and try to test this component, add the properties, styling it. You could spend around a week building a real reusable dropdown. And just to mention a list of topics that I did not touch upon, but you might look into it. You might want to look into the shadow DOM, shadow DOM. So, this would be an isolated CSS namespace only for this component, so if this is a reusable component, people use, we make sure that our CSS classes do not clash with their CSS classes.
The other thing would be looking into virtualized lists. They will allow you to, if you have thousands of options when we render, only render the ones that are visible and as the user scrolls, show or hide other ones. So we do not have games, memory leaks, because we have too many components. So these are two super advanced hyper advanced topics you could apply. I'm not going to do it in this video, because again, that would take me around 45 minutes.
But I can do a longer session when we go in depth. So this is pretty much uh what we've done. Let's see our diff. Basically, as you see, we added the loading and error state, we make sure we have the appropriate state transitions. We apply separation of concerns and split this into two uh components and we memorize the presenter one, which makes it a lot better, like a performance wise. And uh the other thing we talked about, right, more advanced accessibility and building this without native elements.
So, this is how a senior will look at the code, and whenever you have um an interview where you need to refactor code or you need to talk about code, feel free to take this first of all functional requirements and then non-functional requirements principles and make sure you never run out of things to say. Senior developers, senior candidates are very proactive. They know the best practices and they can move the needle forward, that's what's expected of them.
I'll see you. Linux one.