Check out a free preview of the full TypeScript Fundamentals course:
The "Challenge 8: Solution" Lesson is part of the full, TypeScript Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:

Mike walks through the solution to build stateless functional component for a search result item.

Get Unlimited Access Now

Transcript from the "Challenge 8: Solution" Lesson

[00:00:00]
>> Mike North: We're going to add this place search list component to this app so that we can start to see different states of like having just type something in waiting for results to come back and then seeing the results themselves come through it. All right so and this is, let me get my branches straight here.

[00:00:23] So that was previously exercise seven, so this is eight.
>> Mike North: So I've got a nice, clean branch here to work with. And we are working in autocomplete-2, so we're in this folder here.
>> Mike North: So the first thing we want to do is we want to start running this app, right.

[00:00:46] So, we're going to say NPM start autocomplete-2. And that'll fire the app up. And at this point it is running on local host 3000, so if we refresh this is what we see by default. This should be your starting point. I'm just gonna drop it in so we can see that as we go.

[00:01:08] And what we're seeing here, so we'll start with where the app sort is bootstrapped. And we can see we're rendering an app inside the root element. This file doesn't really change much from exercise to exercise the app component is different. We introduce this as we kick this exercise off.

[00:01:28] And then the place search result list, this is where we're stringifying whatever arguments are passed into this component. Now I could implement this as a sate list component if I just let the outer app handle all of that state.
>> Mike North: I'm going to make this, well, I'll leave it stateless I guess.

[00:01:50] We can treat the outer component as the thing that is dealing with the state. I'll just convert this to an arrow function, obviously we've got an any there that we wanna get rid of. So just for fun I'm gonna create a new interface here.
>> Mike North: And I'm gonna call this a result list props.

[00:02:18] And it's gonna look a lot like the app state.
>> Mike North: Right? We need to know whether, we basically need all of these things plus something else. We need all of these things and we also want to have a search function to pass in. So what I can is export this.

[00:02:42] And just extend the interface in here.
>> Mike North: And vias code will let me, it'll resolve that import for me. So that's gonna be the interface that I expect to be pass in. And the one thing that we're looking to see here is, I wanna say it's an optional function, that can be used to handle that type in there.

[00:03:12] I mean I make it optional just because like for custom purposes maybe we just wanna pass a term indirectly or resultless. And I don't want this to be like a hard set thing that I absolutely must provide when I create this component.
>> Mike North: So this is going to be a function that takes in a string and I don't care what it does.

[00:03:39] I don't care what it returns.
>> Mike North: Give ourselves some more space here. Okay, so now, the benefit of having structure around our props here is I'm just gonna call this low case p for props. And that lets me do something like this. Where I'm gonna see that state come through, sorry, I'm gonna see those props come through as soon as I make TypeScript happy.

[00:04:10] Now, where it is unhappy right now is, I've now set this component must be past a certain number of things, or you're not allowed to work. And down here obviously I'm not following through on that. So the first thing I want to do because the things that this place search result list needs.

[00:04:31] They are a super set of the state on this component at least for right now. This kind of works. Now if we added state to this app component that this place search result list needed. We'd probably have to have another interface in here that was shared across these two things.

[00:04:48] And things would get more complicated. For right now it's pretty simple. So we'll first spread this state. So you're basically at everything that's on the state object and in addition to that. We wanna provide this handle search function and we'll put something there and actually I can just pipe this right in this.trysearch allright ,so now we're happy again.

[00:05:26] So, what we gonna do here is, I forgot to do one last thing. So, when we pass a function in like this and our intent is to hook it up to an input element, we're gonna run in to a problem. And that is when, like you connect a function to an input element, usually the domain in itself or the window is the calling context of the function when it is invoked and we want that, in this function here we refer to this, we refer to the calling context.

[00:05:57] We can do something here to enforce this to kind of make sure we set things properly.
>> Mike North: And say I the colon context here must be App, it is of type App.
>> Mike North: So now like as soon as I hook this up, when I try to bind it here, I'll bring in the HTML, we'll run into the error and I'll show you how we can fix it.

[00:06:28] So, the next thing I want is the structure of my HTML. I'll get that from my snapshots. This looks reasonable. Oop, that was not a copy, was it?
>> Mike North: Come on clipboard, work for me.
>> Mike North: There we go. All right, so we can get away from that.
>> Mike North: And we're gonna hook up to this on change event.

[00:07:08] Let me fix this up a little, so it's not horrendous to read.
>> Mike North: Snapshots are great but they don't make good copy and pasting I'm finding out.
>> Speaker 2: Can't you highlight it and do format selected whatever it is.
>> Mike North: Yeah, I probably can.
>> Speaker 2: It doesn't always work.

[00:07:33]
>> Mike North: But, not quite. I can't do, there is key, a key code that I've. So my key code of the week to try to remember for [CROSSTALK]
>> Speaker 2: Command J for me.
>> Mike North: For collapse into one line?
>> Speaker 2: Yeah.
>> Mike North: Yeah, so if we do that. So beautiful!

[00:07:53] That's my tip of the week. Or what I'm trying to remember this week. All right so now we've got the Rough structure of this component. And this is just an error message, we don't need to give it any more space than it currently has. So onChange, we'll just hook up, and we know that our props object has this handle search function, and it's gonna complain to us.

[00:08:14] Because what it's basically telling me, if we were to read into all of this, and why don't we read into it, by the way? We can see that a change event handler. Okay, so we're diving into what the value of this unchanged thing is expected to be. So this is a change event handler, which is an event handler, which handles a synthetic event.

[00:08:37] And basically, we're not guaranteed at any point that we're gonna get the right calling context. There are no guarantees here.
>> Mike North: So, essentially, like, actually, let's see if we can dive in a little deeper here. Is this gonna.
>> Mike North: Sometimes they'll even declare that they're gonna be called in the window context.

[00:09:05] But I don't see that here.
>> Mike North: What I do see, though, is that handle search is not going to work in this situation. And we would also need to do this, event and then
>> Mike North: And this is possibly undefined, so here's what we can do.
>> Mike North: Up here, we'll just say, Let handler =.

[00:09:46]
>> Mike North: handleSearch or.
>> Mike North: Nothing.
>> Mike North: And this is complaining
>> Mike North: There we go. It's too many parenthesis here. So essentially, and I can optimize this more and say like. Const NO_OP = that. Just say. NO_OP. we fall back to NO_OP. So basically, we're gonna bind a function to it either way.

[00:10:23] Only in the event that we actually, like, had something there, could we make that work. There is another way to do it, you could have basically built an object on it, so here's the other way. We could have said handler equals this, and then if, handleSearch, then we will say handler.onChange equals.

[00:11:00]
>> Mike North: I'll make this happen in a second.
>> Mike North: So we could have done something like this. onChange is gonna be a, whatever this is, reaction change event html input element.
>> Mike North: It's not letting me have access to that. So, we could just do, we'd have to, so here's the overall strategy.

[00:11:46] Build an object and spread it, and like, if there's no, if it's an empty object spreading it does nothing, if there's a property on that object, it'll add that key value paired to whatever you're passing it into the input. As you can see, this is complicated enough that it's like, getting pretty fugly here.

[00:12:04] So, what we're gonna do is just go back to the original one. Handler equals this or NO_OP. We fall back to the NO_OP like this, so no matter what, this is a function, and now we're saying the calling context avoid is not assignable to this of type app.

[00:12:26] Finally, we've run into a situation, where we've been reminded of, one of these things about react is when we bind functions to dom elements, or when we connect functions to dom elements, we have to make sure that in the component, the context in which they're supposed to be called, we have to take care of packaging it with its calling context.

[00:12:46] So this is the typical pattern you see over and over.
>> Mike North: And now it should be happy, come on.
>> Mike North: No, you're not happy?
>> Mike North: This calling context of void.
>> Mike North: Hm. All right, we'll let that go for a moment. I'll see if I can get that working, when we come back.

[00:13:25] So in any case, what we should now see is that we can type something in, and we should see that this function gets called, and we will.
>> Mike North: My dev tools hooked up to the browser again. So we should see that if I type something in, there are some fetches going out, right?

[00:13:48] And we see some data coming back, and this, you know, conceivably is businesses that start with the letter A. So great, that's fantastic. So now it's time for us to deal with the various scenarios that we could be in. So really, like, it is the contents here that are probably gonna end up changing.

[00:14:10] We have a list of search results, and you can see that in this first scenario, we've got like an indication that we're searching for something. It's possible that we can kind of abstract away, like whatever this list of things is, into a variable, and do some switching based on state, and then ultimately render that variable in there.

[00:14:30] Something like this. Let result set equal that, right? So in this situation, like and this will, sorry it's so narrow here. This will work just as it has been, right? It's the same exact thing that we're seeing here. But the difference is now, based on whatever we want, we can put different things in this result set.

[00:15:05] So, for example, and we've got everything that's available on this list of props, whether we are in progress or the search term is empty or whatever. We've got that in order to make this work. So we can just say, let result say equal. Let's look at its type.

[00:15:27] JSX element. So result set, we'll create an empty array, and then if the search term is empty, we can say result set, push, and we'll just copy this.
>> Mike North: We'll say
>> Mike North: please enter something.
>> Mike North: Now, else if (p.inProgress), we could do something similar here.
>> Mike North: Saying this time searching for,

[00:16:24]
>> Mike North: p.term, so let's see if just those two things work for now. Please enter a search term above. Thanks automatic, well, that wasn't automatic semicolon, that was developer semicolon. So coffee, awesome, so now we're in the third state that we failed to handle which is we're no longer in progress and we have some results.

[00:16:47] So else, we, I'm gonna be paranoid here and say I don't wanna assume that this is the only other possible state that we could be in. So else if p.results,
>> Mike North: length > 0, now we can say results.map and here we'll say, what's the name of this other component that we have?

[00:17:25] It's a PlaceSearchResult.
>> Mike North: So there's our PlaceSearchResult. We can tell it to import for us, and now-
>> Mike North: We have an error, what's going on?
>> Mike North: I need a key. So any time you're dealing with React arrays, you have to basically tell it. You have to give it a unique identifier for each item in the list, so that it can know which one to re-render in the event that the size of the list changes.

[00:18:09] All we have to do here is say key=, what would a good property be, r.id, that seems like it might be unique.
>> Mike North: And it's not rendering. I wonder why, you know why?
>> Mike North: We'll just say the results that equals those.
>> Mike North: And apparently it's gonna want, because it's in array, we'll call this one nothing.

[00:18:56]
>> Mike North: Sorry, nothing and this one's in progress.
>> Mike North: So even those that are in an array, those'll make it happy. Great, no more errors, and there we have coffee or,
>> Mike North: So now we see our results coming in. So in essence, we have used typescript to basically ensure that all along the way, we're attaching the correct types of functions to the correct dumb events.

[00:19:25] We have assurances around these components to get in the data that they state that they need to get. We even have this idea of an optional like event handler that we pass in. Where we're not obligated to continually pass a no-op functions into this just to run test, or anything like that.

[00:19:44] So this can work with or without it. So this is really, really nice, and it's super clear when working with these components. You know exactly what it needs up front and you're busted when you don't play by the rules. So this is state for components and if there any questions online or here, I'm happy to answer them before we go with the next.

[00:20:07] One more inception deeper into this thing.
>> Mike North: How do people feel, for those of you who have used React without typescript? What are your thoughts on this? Is it annoying extra bookkeeping or is this additional safety? How does this, how does this feel? Yeah, I think so I like it, all right-

[00:20:36]
>> Speaker 2: It helps you comment into it and to see what you need in order to render that template like new coders, new programmers who didn't necessarily write it. You come and you're like, this prop requires this interface. And so if I want to use this component somewhere, I'm gonna have to hand it these keys.

[00:20:55]
>> Mike North: Absolutely, absolutely, not just the type, but also the requiredness of the arguments by default. And so you're not worrying about multiple error ID functions that, it feels like the rules are stated up front. And the more experienced I get, the more I appreciate constrained programming environments. I want to know what my constraints are so I can design a good solution.

[00:21:21]
>> Speaker 2: So much better than the PropTypes thing, so much more articulate.
>> Mike North: Not only is it more articulate, but it's more performant. PropTypes, which solve the problem in a similar way, that's that run time, right? That's extra weight that you're sending to your users, and it's gonna slow your app down, potentially.

[00:21:43] And I don't feel like the feedback you get is quite as nice.
>> Speaker 2: Well if it's rich at all, it's really hard to write the code. Sometimes they're very hard to collaborate.
>> Mike North: Yes, any time you're wrestling-
>> Speaker 2: Any graph of objects, it's really
>> Mike North: [LAUGH]
>> Speaker 2: [LAUGH] Yeah, I have been there for sure.

[00:22:01] All right, so let me commit this so anyone who wishes to use it can take a look.
>> Speaker 2: 8,
>> Speaker 2: Okay, exercise 9, second to last one.
>> Speaker 2: So you can if you so choose, check out femasters/9-begin and you will get the solution that I just checked in.

[00:22:41] And this will serve as the starting point for this next phase of the project