Working with External Libraries 5 exercises
solution

Creating a Generic Wrapper for useForm

Currently, we don'y have any inference for our form:


const customForm = useCustomForm({
firstName: "",
lastName: "",
});
// hovering over customForm
const customForm: {
register: UseFormRegister<any>;
handleSubmit: UseFormHandleSubmit<any, undefined>;
getValues: UseFo

Loading solution

Transcript

00:00 Okay, let's put our generic function hat on. We know that we are going to need to capture these values somewhere in a type argument because currently the things down here, they aren't working because custom form is just being inferred as any. There's no inference going on. So we need to capture those.

00:15 So let's put them in a type argument called tValues. And tValues, that is going to extend something. So I'm just going to make it extend any for now. And then we're going to capture those in tValues. Now, let's take a look down here and see if we can get these first tests working.

00:34 We've got default values must be an object. Hmm, okie dokie. Now, that's interesting because like use form up here, if we take a look at this, it requires something in the shape of field values. Let's take a look. It must extend field values. So you can't pass in default values as like a number or something.

00:53 Now, we can actually use this type to our advantage. Why not? It's just sitting there, right? So why don't we just say instead of any extends field values and grab those from React hook form. So now then, if we look, it's actually working really nicely already.

01:09 We've got our custom form here and it's actually understanding everything. So we've got use form register, first name, last name, use form handle, submit first name, last name. And that's because this is actually automatically working. This use form hook actually takes in tValues. So whatever we pass in here just gets put into use form.

01:29 And now form.register, it's got tValues in there already. So we don't even need to annotate the return type of this. It can just work as it is. We will do in a second, but we don't need to, which is beautiful. So this default values now, type default values is not assignable to type deep partial or async default values tValues undefined.

01:49 There's something really strange going on. And the only way I could get it to work, because we know that this should work, because use form itself, it's constrained to field values. This tValues is constrained to field values. So we know it should work. So what I ended up doing is I ended up doing as default values,

02:11 let me see, tValues. And this, what it does is it just fixes this little strange paper cut because it seems to be that when you use default values here, like use form just isn't quite working here. And I think this is slightly probably on the fault of the maintainers

02:31 or on the builders of this library. And when you actually go to use this in your production apps, like this may actually just be fixed here, which is kind of interesting. So like what I think the lesson here is, is don't be afraid to use as on these integration points.

02:47 Because what we're really doing here is we're just sort of fixing an error here in line. We could have also said that as any, but actually if we do as any here, it actually breaks. Because now all of the stuff from use custom form here, it actually just like returns any on all of the things here. So use form handle submit. So we really do want to be pretty precise here.

03:07 And the other way I debated about fixing this is by using the other technique, is by saying tValues. But oddly enough, it doesn't work as well. So we still have to do as default values here. This has a similar effect, right? It's in fact exactly the same as just sort of passing in the inference here, except for it's just a few lines of code extra wasted.

03:26 So we don't even need to use it because it understands that tValues is being passed to it no matter what we do. So yeah, this little paper cut here, I think the lesson is don't be afraid to use as in these integration points to get things working as you need them to. And let's just quickly type the return types here. Now, I think this is a slightly futile exercise

03:46 because we don't really need to type them. They're pretty well typed already. But if we wanted to, we could say, so we've got useFormRegister tValues. So let's put that in here. So register is useFormRegister and then tValues. Nice. And we'll just grab that from React Hook Form.

04:06 Next one is handleSubmit, which I assume is useFormHandleSubmit tValues. And then the last one is getValues, which I assume again, form.getValues, useForm.getValues tValues. And it's nice that you can just sort of hover over these. And it's also nice that React Hook Form actually grabs this stuff for you, which is really good.

04:27 Use, what was it? useForm.getValues and then pass it tValues. Sometimes you'll find in external libraries, these won't be available on like an export like this. And you'll need to do some sort of transformation to get them. But this all seems pretty good. And all of our tests are passing. So hopefully this gives you an idea of how you can,

04:47 like techniques you can use to fit together different external libraries in order to get them to play nicely.