Calling useState with Excess Properties
In this exercise, we will explore a strange property of useState
, and how TypeScript treats it.
Here we have a complex component named Tags
, which is different from the previous one with the same name.
interface TagState { tagSelected: number | null; tags: { id: number; value
Transcript
0:00 In this exercise, we're going to be looking at a strange property of useState and the way that TypeScript treats it. We have quite a complicated component here, so I'm going to walk you through it.
0:11 This component is called Tags, and it's slightly different to the component we had before called tags, where we have a tagState, and this tagState is being captured inside of useState here. This has two pieces of state in it. It has a tags, which is an array, and it has a tagSelected, which is a here.
0:30 Now, you might think you could break this out into two different useStates. I actually like keeping it in one place, because for this pattern, we have our tags here, which we're mapping over and we're returning a button with the tag.value inside it, and when we click that button, you can basically select that tag.
0:47 What we're doing is we're using this API of setState where you can actually pass a function to setState to check on the current state and return a new state here. Very nice. It means that you don't have to...It's just a little bit cleaner here in many situations.
1:02 We also have the same pattern down the bottom here where we have a button, which is onClick, to add a new tag, and this one adds a new one to the tags here. It also just retains the currentState as well.
1:15 Except there are a couple of errors here that we're expecting but we're not getting. If you notice, in this first setState up here, this tagselected thing here is actually in the wrong case. It's actually all lower case, whereas this one is in camel case. We have tagSelected. That would be correct, but it's not written like that. It's actually written tagselected.
1:39 I've added a couple of @ts-expect-errors here, because you would expect this to fail, but it's not failing. Same down here. We have a function that returns tags, and these tags, I'm passing anotherValue into there. I can actually pass stuff that isn't expected by these tags. I can pass anything I want to in here, which seems super-duper weird.
2:01 I've not messed about with this in any way. I've not screwed up React's type definitions, but something strange is afoot. This is a funny exercise. I'm going to ask you find a solution to this problem, but this is also a problem that doesn't really have a solution.
2:17 What I always do is just dive around, see if you can make it work, see if we can get these errors working. I'll show you a way to do it. There is a way, although it's not very ergonomic, and then I'll explain why this happens in the solution.