June 2nd, 2021 × #react#typescript#webdev
React + TypeScript
Discussion on setting up React with TypeScript, typing components, props, state, hooks, and other common patterns when using the two technologies together.
Transcript
Announcer
You're listening to Syntax, the podcast with the tastiest web development treats out there. Strap yourself in and get ready. Here is Scott Talinski and Wes Bos. Welcome to syntax, the podcast with the tastiest
Guest 1
TypeScript and React treats.
Introduction of today's topics: Using React with TypeScript
Guest 1
Ready. Today, we've got a really good show for you. We've been doing a a whole slew of TypeScript shows lately, and this is the show that is entirely on Using React with TypeScript. So we're gonna go through pretty much everything as to, like, how do you set it up and and how do you do components and state and props into passing data, custom hooks, and ESLint, all kinds of good stuff. So we are sponsored by 3 awesome companies. Re 1st one is FreshBooks cloud accounting. 2nd 1 is Sentry error exception and performance tracking, and the third one is Linode.
Sponsored by FreshBooks, Sentry, and Linode
Guest 1
Rid They are cloud computing developers trust. They have a Linux servers you can use to run literally anything you want that runs on Linux servers. We'll talk about all of them partway through the How are you doing today, mister Scott Talinski? Hey.
Guest 2
I'm doing good. It's a it's a rainy Monday morning here, And I'm just, I'm I'm kinda digging the vibe. You know? It's raining here too. Do you ever listen to the, the, you know, the low rid Chill Lo Fi beats to study 2 on YouTube. Do you ever listen to that YouTube channel? Once or twice, I believe I have, but I I know the vibe you're talking about. Yeah. It's that specific vibe, and that's exactly what's going on over here right now, and I'm liking it. Yeah. Yeah. I could do some of those low five beats right now.
Guest 1
Yeah. I I've got the windows open right now, and I just love when here in the rain. Oh, yeah.
Guest 1
Good stuff. Let's get into it. TypeScript and React, fan. How about you?
Guest 2
I'm, I think I'm a pretty big fan. Yeah. I think I'm a pretty big fan.
Guest 2
At least, for the most part, I think, re when I was first started getting into it and first started learning it, I don't know if there was the same level of resources out there. So some of it was a little bit you'd hit problems in rid I gotta go to Stack Overflow and figure out what's going on. But I do wanna say before we get too deep into this, a lot of these notes are going to be coming from the re repo that has, the React and TypeScript cheat sheet. So we'll make this link available to you in the show notes, But this is, just a fantastic, fantastic repo that has really just I mean, if I wanna see At least best practices or good explanations for everything that we're talking about in here. This repo is really the the place to find it. And Once this thing came out, I know Swix was a part of it. I don't know. Did he author the whole thing? Or no. I think there's there's a a bunch of devs behind it. There's a bunch of devs behind it now, but I think he might have created it initially. Oh, yeah. But, yeah, it's it's, it's fantastic. So give this thing a look. It's gonna be in the show notes. Definitely well worth your time. Yeah. I have really enjoyed
React and TypeScript integration is polished now
Guest 1
writing my React apps in TypeScript. I feel like it is at a point now where it's pretty polished.
Guest 1
There's some stuff that you get into in TypeScript and and even like, I've been doing a lot of, rid I call it vanilla TypeScript, which people don't really understand. I need a bit of a better name for it, but, like, what I mean by that is Doing a lot of, like, vanilla DOM stuff, so just using built in browser APIs Mhmm. But doing it in TypeScript.
Guest 1
And for the most part, that stuff is pretty well pretty well typed, but I I do hit little gotchas here or there or, like, The other day, I had to type something as unknown as the type name, and I was like, this has gotta be wrong. Basically, you're forcing it into unknown and then rid back into the type that you want it just to get the TypeScript compiler.
Guest 1
And I was like, oh, okay. And I I, like, asked around. I was like, what's the best way to to actually do this properly, and everybody's like, that's kinda the best way. And I I don't feel like I hit a whole lot of Stuff like that in React. So, yeah, it's a it's a good place to be right now, I think. Yeah. And I I actually you know, read. Authoring React code that has been typed
Guest 2
is really one of the best experiences in TypeScript for me. Like, it was definitely a wow moment for me when you all of a sudden see a component that you haven't touched in 6 months or something. Like like, this is an old component. You You just kinda open it it up. You're looking at it, and then you see it's underlined in red. And you see it's being passed a slightly different prop or maybe even outdated prop. And re It might not have surfaced as a bug in your code because, hey, this is just an extra prop being passed into this thing, but it's not rid Part of your code base anymore or has been changed, and and TypeScript is able to find those bugs way, way better, I think, than than we were ever able rid to keeping the whole thing in sync by making just changes whenever it came around. Alright. So let's kick it off with components.
Guest 1
There are rid A number of different ways to type a component in in JavaScript, and I primarily just stick to rid Regular old components, the the word function, the name of the components, parentheses, all the props that you wanna take in, and then The returned value from that. So for that type, what do you what do you even call that? Just a function component, I guess? Rid Yeah. I'd say so. That's all I do. They used to be called stateless functional components. Now they got state. Yeah. Then we got hooks, and now they got states. So now they're just rid. Functions that return some some JSX. Now they're just components in my mind. Yes. If you make a component, that's, like, the base way to go ahead and do it, And they're pretty simple. At the the very highest level is that you you take in props. We'll talk about typing props in just a second. And then from that, you return some JSX, and you can type those as JSX dot element.
Typing basic function components in TypeScript
Guest 1
So React, when you're when you write React code in TypeScript, you use a dot t s x file. And when you write regular TypeScript, you use a dot ts file, and those are completely separate, which is funny because When I think in the early days of React, people started using dot JSX file names. Mhmm. And we've sort of gone away from that, And now it's come full circle and that we're using dot TSX files for authoring React.
Guest 1
Put that on ice for a second. I'll I'll I'll ask you what you think of that.
Guest 1
But rid. React gives you a namespace called, jsx dot, and then there's a whole bunch of different elements on there. And the the most basic one is dot element, capital e. Yeah. What do you think about the JSX
Guest 2
and dot TSX files? I like it. I like that it's explicit now that it's JSX or TSX, and I, was the type of person who was writing JSX in a dot JS file, and I kinda I don't regret that because rid It's not like I'm doing it still, but, like, I don't know if I would do that again. I think I'm firmly in the camp of, you know, a dot to JSX or a dot TSX file indicates that it is a JSX file. Yeah. And using the dot JS for that extension really doesn't have a lot of benefits.
Guest 2
Besides, I don't even know what the benefits are, to be honest. But, before, I thought it was clean. I didn't wanna have to rename a bunch of my files or something. I don't know. Yeah. It's funny because I was kind of against it. And I think initially,
Guest 1
because a lot of the, like, plugins didn't work in For, like, Versus rid in Sublime and yeah. Because it, like, in it, like, detected the what what you're in. But now I think about it. If you're writing rid. Jsx in a dotjs file, that's not really JS. It's JSX. Right. So maybe I'm swinging back to the other end of things now and saying, rid maybe dot t s x. And it it certainly helps a lot of things because now we have all these babble plug ins where it just rid. Sometimes assumes that you have JSX in your JS files, and you have to run that through it first to convert to, what, create class, something like that. Yeah. It's interesting. I don't know why I ever felt
Guest 2
that that was cool to use a, you know, JSX in a JS file, but I did. That's fine. Do you typically
Guest 1
when you are writing your components, do you typically have a return type in yours? I think and and this rid. This might change. Like, we might have a a show 6 months a year from now. But for something as simple as that, I let TypeScript infer the type from that Just because it's it's less typing. And, also, like, when I initially got into writing React, you had the props, rid. You had the default props. You had the type of the props.
Guest 1
Often, people would create the type for the props in line, and then you'd also have the return type. And especially to somebody who's new, you kinda look at that and go, what are all these pieces with all the curly brackets and colons and equal signs?
Guest 2
Rid So I for very basic components that are just JSX element, I let that be inferred. What about you? Yeah. I typically do, I think, as well. There rid Has been some benefit to having it be typed. Some of our components use the React FC or React function component, which found a pretty convincing argument for why it's should be discouraged to use that, but one of the benefits of that is you do automatically get The return type of JSX element so that if you're not return you know, you ever get those errors in your code that's like You're trying to load a component that's not a function or that's not a a component. You're you know, might be whether that's an object or whatever that error is. I I'm I'm I don't know the error offhand, but I've seen it at time to time. Basically, when you're trying to use something as a component that's not a component, I found this can help with that, Giving it an explicit return of JSX dot element, but I haven't used it really myself, outside of the f c function component,
Guest 1
generic. Rid So what's the argument against using that then that you're saying you found a pretty good argument? So this argument was found on the
Guest 2
ripped. Cheat sheet, and it was in a issue linked in the the cheat sheet there. And they're basically saying when you use The generic type of FC or function component, which by the way are the exact same thing, one is the shorthand.
Guest 2
That's a little tidbit for you. I always just use FC Things like as well as the return. So like children automatically becomes typed. Right? But the argument against that is, you know what? Explicitly, children should probably be defined on its own because not every React component rid have a children prop Mhmm. Or is it appropriate to be used that way? So they're saying if you use f c or function component, Every component that you make will automatically have a children prop regardless if it's used or regardless if it's accurate.
Guest 2
Therefore, it's not going to fire an error if you pass children into a component that cannot accept children.
Guest 1
Interesting.
Guest 2
It may be one of those things to be a little bit cognizant of rather than, you know, maybe dictating your life over it. Although, you know, I've never hit this bug myself, but it does seem like a pretty good reason to not use it unless you know what you're doing. If you are using class based components, there is a,
Guest 1
a React dot component that takes 2 generic, arguments, your props, and your state just like, it normally would if you're using a class based component. And then you can go ahead and type what you expect the state of that component and the props of that incoming, Incoming props are going to be it's pretty pretty straightforward. Like, a lot of the stuff in React, if you already know TypeScript, You're just gonna kinda take a look. Okay. How do I do this? Alright. Okay. Makes sense. Next. Like, it's not like, oh, this is a totally different way. It's it's very much in the vein of This is how TypeScript works. Totally. Which makes sense because it is TypeScript.
Guest 2
I think that makes sense. Yeah. That's funny. Yeah.
Guest 2
So okay.
Guest 2
When you're typing your components as props, I'm interested in knowing what your Strategy is so if we're getting into typing components as props, you can think about components themselves are just functions. Right? And functions have parameters. They have arguments, And you can type those the exact same way you would typically. So you could potentially create a type of, you know, type of props. I usually if I'm actually typing my props, I usually do type capital p props is equal to, and then the props, And then I define the prop types there, and then I will in the either destructuring or when you use props, I usually just destructure the props out. You give it a value or you give it a type just like you would any other parameter. So I typically create my types outside of the function In its own thing, type of props, and then my props are colon type of props. And that's worked out well for me For the most part, but that's typically how all of our our props are defined for individual components.
Guest 1
Yeah. I'm I'm on the same page as well. So just to be clear, Because React components, generally, people don't just pass the props object And then access props dot age, props dot counter, props dot fetch another joke.
Guest 1
People generally Structure those properties into their own top level variables with that's why you see the curly brackets when someone defines a function. And because When you destructure something in a function in TypeScript, you cannot type those individual destructured items Individually, in your function definition, you have to you have to type the entire argument or the sorry, the entire parameter in that. So you have to basically destructure, like, joke, age, counter, and then colon, and then you have to give it the type rid for all 3 of those things in one go. And that can get a little bit hairy because you're often destructuring 3 things.
Guest 1
You're Typing 3 things, and it's you're creating a new type by putting curly brackets. And then sometimes you also have default parameters thrown in there as well and a return type, and then we're just like, okay. This is this is very hard to read. So Mhmm.
Guest 1
Much prefer to just type them the line before the function. Just create the type. If if you're using them in more than 1 place, then, absolutely, re You can export them or throw them in a separate file and import them if you need them. But, generally, yeah, I'm I'm doing the same thing as you. It's just typing them right before
Guest 2
The actual function, just for my own sanity stake. Yeah. Did you know that the default props can be inferred? So, like, if if if you have A optional prop that's a type of string, and you have a default value of it. So on that destructure, you're setting a default value.
Guest 2
Yes. Let's say you have a prop, and it would be like name. Name is equal to and then quotes Scott in that, in that destructuring.
Guest 2
Rid. TypeScript can infer without you having to explicitly type it that that type is going to be a string.
Guest 2
Of course, that's not going to necessarily work with Ones that don't have any sort of default values,
Guest 1
default parameter values, or anything like that. So that can happen. And and for very simple components, sometimes, like, That is enough. That's more than enough, and and that's all you need. And other times didn't know that. It can be a little tough. Yeah. I didn't actually didn't know that at all. That's kinda cool. Because so that is a perfect use case for if you have, like, I've got a little example in the notes when rid Scott and I are talking right here. We have a joke, and the joke has the text of the joke, and it has an ID, and it has a status. So it's string string number. And instead of having to type out that it's string string number or if you're just passing, like, an ID and some text to something, Then you can set some default values there, and it will infer that it is a string in string. I think that having known this for 1 minute, rid. I think maybe my rule would be that if it's the built in types, string, number, Boolean, it would be fine. Yeah. And then, obviously, anything anything any custom rid types. I don't think you would be able to Exactly. Do that. I don't know if I would recommend it for most most situations.
Guest 2
No. But it is an option in your tool belt if you Want it. Yeah. It's kinda like the, one liner implicit return, no parentheses.
Guest 1
In most cases, you probably don't want to use that, but It can be really handy in a lot of little map, whatever, quick little use cases. It's so handy to be able to do that. Totally.
Guest 2
So that's that's gonna work for default props, but not if your props have some fresh nondefault data. I don't know. This is not great, but, what it is is a transition into FreshBooks, which is one of our sponsors for this episode. Wes, do you wanna talk a little bit about FreshBooks? I know even though it's June now, technically, it's kind of like tax time. Right?
Guest 1
Yeah. Well, in Canada at least, our taxes are done, Thankfully, I just got pushed back a little bit. Last year, ours got pushed, and they just kept getting pushed and pushed and pushed. Mhmm. And this year, they did not get pushed, which people are all up in arms about. But they wouldn't be up in arms about if they used re FreshBooks. Hey. Hey. So FreshBooks is a crowd favorite for cloud accounting. It has an intuitive dashboard rid. Easily navigate over. You can log all your expenses. You can see all of your outstanding invoices, who you've invoiced, when you invoice them, automatic Send, reminders for those people that need need the invoice sent to them, and they, oh, I forgot to pay it. FreshBooks will do all of that for you, so you are just on your taxes. Now this is the year. This is your year. You need to start fresh. Switch to FreshBooks and join over 24,000,000 people who use it and love it. It's super easy to get up and running with award winning support. You are never alone. I can attest rid That I've called support a couple times. Try FreshBooks for free for 30 days. No credit card required. Go to freshbooks.com/ rid Syntax and enter syntax in the how did you hear about a section and start thinking what you're gonna do with all that extra time you're saving. Thanks so much to FreshBooks for sponsoring.
Guest 1
Sick.
Typing React component state in TypeScript
Guest 1
Sick. Know what else is sick? State.
Guest 1
Oh, state is rid Super sick.
Guest 1
So, how how do you type state, Scott? So state is actually pretty easy.
Guest 2
Believe it or not, you could actually have state inferred as well for basic state types. For instance, if your basic state type is rid Like, let's say it's a boolean, is toggled. You have a boolean is toggled. Set is toggled are the 2 props the or the parameters that you're destructuring from the result of your use state call. Right? So it's a use state And then in parens, the boolean value. If you have a very simple, setup like that, TypeScript's gonna know that it's a boolean, and you don't have to do anything extra.
Guest 2
Sometimes, they can be a little bit more complex whether they have an object, maybe an array of stuff. Maybe you just need some very specific types of values in there. You have some types to find elsewhere, Well, you can actually pass these right along through using the syntax with the angle brackets where you would say use state, rid. Then you have the angle brackets and then whatever the type is of your state that needs to be applied to the state's value.
Guest 1
And then React will take care of the rest in terms of the function to update your state will be typed automatically for you via that, rid. All through the use of generics. Yeah. That this is a really, really good use case for explaining what generics are Because the function is use state, but you don't wanna create any new function for every single type of state Right. That you would have. So you just pass the type of your state to the angle brackets of use state. And then, also, like, when we we did the TypeScript, a bit deeper dive a couple weeks ago. Mhmm. There was I said I said that the type can be inferred from a generic, And a couple people asked for an example of that, and I I tweeted out an example just with regular JavaScript. And a couple people said this is, yeah, this is how React does it. So if you pass Even, like Scott, you said if you use use state and you pass it like a Boolean. But if you pass it any value Yeah. That is already typed, it will infer that type from it. I think I still prefer typing it out just because you can see it without having to hover over the type to see what type it is. But, yeah, it's it's pretty simple. And then from that, you have you are returned an array With it, which is user and and set user or, jokes and set jokes. Basically, you know how how use state returns you an array. The first item is The state itself, and the 2nd function is the updater.
Guest 1
Mhmm. And those things will, obviously, automatically be ridged, and you'll have access to all the properties when you start to go ahead and use that state
Guest 2
inside of your inside of your application. Yeah. It's pretty simple. Rid. To be honest, you look at some of this, so you get your 1st TypeScript error, and that error has to do with the, you know, the re Type being on that function. And the the type error that comes in that case could always be a little bit confusing, and you say, oh, great. Now I gotta go down this rabbit hole. But In reality, with TypeScript in state in React, unless you're getting into use reducer or even context is pretty simple if you know what you're doing, Once you get into, you know, the actual process of this using, these generic resolve values, it is Very, very simple, and it reads pretty well in my opinion, which is not something that I thought I would say when I first started looking at TypeScript and React. That was one of the rid. The 1st barriers to entry was just how many extra characters it was to do certain things, and once you get over the clutteredness of some of that, it does read pretty well you train your eyes to it.
Typing side effects with React useEffect hook
Guest 2
Next step is going to be useEffect, which useEffect is commonly used for life cycle in React. Right? Rid. Side effects, they say. If something needs to run on when something is changed, maybe unmount, maybe unmount, those types of things, this is when you use useEffect. Now The cool thing about React and useEffect is, hey. You largely don't have to do anything. React has useEffect typed correctly, and for the most part, I I've never done really anything special in TypeScript with useEffect specifically.
Guest 1
Yeah. Because it it just takes in 1 argument, which is I think it takes in 2 arguments. The the first one being the function that runs and the second one being the the list of dependencies, And then it returns the cleanup function if you do return something.
Guest 1
This is not specifically TypeScript Related, but whenever we did the show on nothing, and we talked about the void keyword Yes. And we said I think we both said, like, I've never never used the VOIP keyword in my entire career. Oh, I use it in in oh, you mean in JavaScript itself, not in a type? Oh, yes. Yes. Sorry. Correct. The in the actual in in TypeScript, we have avoid type, which is a function that return doesn't return anything.
Guest 1
Not even nothing. It's I want to ignore the undefined that could be returned from this thing. Right. That's what void does. So there also is a void keyword in JavaScript.
Guest 1
And if you ever have a useEffect that needs to fetch some data on mount, Generally, you I have, like, a little function called get joke, and that's that fires off of fetch request. And when the fetch request comes back, it sets the joke to state. Read.
Guest 1
And inside the useEffect, you'll know that you can't use an await keyword because rid The useEffect function cannot be asynchronous.
Guest 1
So if you wanna use a promise based function inside of a useEffect, rid You have to chain a dot then and a dot catch onto the end. And often, you see people just chaining them on and and doing nothing with them. So if you really don't care about either the results because the result is being put into state or the catching an error, rid. You can pop a void in front of it, and that will ignore the promise that has returned from the get joke, and that will pass any rid Here, let me see what the specific error is. TypeScript ESLint no floating promises is the one that gets me here. Promises must be handled appropriately or rid Mostly marked with a void. So popping a void in front of that, basically, just ignores the promise comes. And a a couple of people tweeted that to us After we did that episode, and I go, That's the first time I've ever run into a situation where I needed to use void.
Guest 2
You know what's funny? It's because I write code like this in my tutorials when I'm teaching React. Right? Yeah. I I actually don't write the void yet, but that's a really neat tip. What's funny is that, like, since using Apollo or React Query, I don't know if I've had to call too many APIs from a useEffect
Guest 1
Since then, isn't that kinda interesting to think about? Those libraries kinda take care of that for me. Yeah. It is it is really nice when a library will do that for you. It's it's true. It's it's only when I'm rid Explicitly running a fetch request and not using a a library like React Query or Apollo, something like that. Yeah. So that that's the little tip there for using VOID. I don't think I've ever seen that or or thought about that. No. Me neither. I was surprised when a couple and, like, it was it was one of those things where rid. 1 person replied to the tweet, and there was, like, a 1,000 likes on it because everyone's like, oh my goodness. That's the best. That's cool. Hey.
Guest 1
I did have to change my ESLint rules to allow void, though, so I had to turn off 1 rule