Supercharging The React Context API, Part 2: Forms
Usage of component factories to improve the quality of your React.js code
- In order to create composable, reusable and readable components, we use component factories that take advantage of TypeScript and the Context API.
- Combining context wrappers, context actions and context form inputs will enhance the readability and reusability of your code by a mile.
What if you could create form utilities that are so modular and reusable, that all you need to compose a signup form from the components of a login form is to define you want the email to be unique!
What if you could create extremely readable components that you can spend hours looking at?
What if anyone reading your component could immediately see in their imagination how it looks like on the web?
Check out the example below:
My name is Itay and I’m a frontend developer who specializes in back-of-the-frontend code, particularly in React.js.
In this article, I’m going to describe my approach for writing form logic in react. Even if you decide to adopt some of it to your codebase, I guarantee it will be much cleaner.
NOTE: This post is heavily based on subjects I wrote about in my previous post, and it uses context logical wrappers and actions.
In this post, you’ll learn about:
- Form stages and form input factories.
In the end, I’ll show you a detailed authentication form example.
The Field Hook
Let’s start with some TypeScript:
We can now use this those types to create our custom hook. From the type definition, we understand the functionality of this hook:
- the hook will manage the field state
- the hook is responsible for showing the errors and providing a method to show them. From that, we understand that the hook will consist of an error visiblity state as well.
- the hook provides no method to hide the errors, meaning it should take care of that on its own.
Let’s see what we can build with that:
If you take this hook and use it in your apps, it will already be a lot easier for you. However, I want to show you how to take your forms to the next level with form input factories and form stages.
Form Input Factories
A kind reminder:
A component factory is a function that returns a functional component. It defers from HOC by not taking a component in the arguments.
Let's start, again, with some TypeScript:
Now that we understand the parameters, let's write the factory. I want to have a way to override the label and the hint when needed.
You can then create a custom context that contains the fields for you form, and let the context wire the logic for everything.
Note that you can add more fields to the form (such as name), and not render their input in the login form but do render them in the signup form - and everything would still work exactly as expected
Let's create out fields:
A form stage is anything that happens while filling out the form, and you don’t know how much time it will take.
Form stages consist of those two types:
- A user filling an input field
- Async validations of the values in the form. For example - validating your email and password on login.
I highly recommend storing your stages in a TypeScript enum, like the example below, and then create logical wrappers to toggle between the stages. Let’s see how it can supercharge our codebase and make it extremely readable.
You can now use form stages to conditionally render Loading messages, or hide inputs that aren’t required in the current phase of the form, creating a “flow” for the users filling out the form.
Let's see the full logic behind the Auth Form Context:
In one of my projects, I created a much more complex authentication form with name and password confirm fields, but the reusability features saved me a lot of time doing that!
- If you wanted to, you could also create a `passwordVisible` boolean value and compose a conditional warpper out of it, creating the visibility option for your password input field.
- You can also upgrade the context action factory, providing a
disabledConsumerargument to disable the button when values aren’t validated.