Create a Custom React Hook to Handle Form Fields
Now before we move on to creating our sign up page, we are going to take a short detour to simplify how we handle form fields in React. We built a form as a part of our login page and we are going to do the same for our sign up page. You’ll recall that in our login component we were creating two state variables to store the username and password.
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
And we also use something like this to set the state:
onChange={e => setEmail(e.target.value)}
Now we are going to do something similar for our sign up page and it’ll have a few more fields than the login page. So it makes sense to simplify this process and have some common logic that all our form related components can share. Plus this is a good way to introduce the biggest benefit of React Hooks — reusing stateful logic between components.
Creating a Custom React Hook
Add the following to src/libs/hooksLib.js
.
import { useState } from "react";
export function useFormFields(initialState) {
const [fields, setValues] = useState(initialState);
return [
fields,
function(event) {
setValues({
...fields,
[event.target.id]: event.target.value
});
}
];
}
Creating a custom hook is amazingly simple. In fact, we did this back when we created our app context. But let’s go over in detail how this works:
-
A custom React Hook starts with the word
use
in its name. So ours is calleduseFormFields
. -
Our Hook takes the initial state of our form fields as an object and saves it as a state variable called
fields
. -
It returns an array with
fields
and a function that sets the new state based on the event object. The only difference here is that we are usingevent.target.id
(which contains the id of our form field) to store the value (event.target.value
). In the case of our form the elements, theevent.target.id
is set as the thecontrolId
:<FormGroup controlId="email" bsSize="large"> <ControlLabel>Email</ControlLabel> <FormControl autoFocus type="email" value={email} onChange={e => setEmail(e.target.value)} /> </FormGroup>
And that’s it! We can now use this in our Login component.
Using Our Custom Hook
Replace our src/containers/Login.js
with the following:
import React, { useState } from "react";
import { Auth } from "aws-amplify";
import { useHistory } from "react-router-dom";
import { FormGroup, FormControl, ControlLabel } from "react-bootstrap";
import LoaderButton from "../components/LoaderButton";
import { useAppContext } from "../libs/contextLib";
import { useFormFields } from "../libs/hooksLib";
import { onError } from "../libs/errorLib";
import "./Login.css";
export default function Login() {
const history = useHistory();
const { userHasAuthenticated } = useAppContext();
const [isLoading, setIsLoading] = useState(false);
const [fields, handleFieldChange] = useFormFields({
email: "",
password: ""
});
function validateForm() {
return fields.email.length > 0 && fields.password.length > 0;
}
async function handleSubmit(event) {
event.preventDefault();
setIsLoading(true);
try {
await Auth.signIn(fields.email, fields.password);
userHasAuthenticated(true);
history.push("/");
} catch (e) {
onError(e);
setIsLoading(false);
}
}
return (
<div className="Login">
<form onSubmit={handleSubmit}>
<FormGroup controlId="email" bsSize="large">
<ControlLabel>Email</ControlLabel>
<FormControl
autoFocus
type="email"
value={fields.email}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="password" bsSize="large">
<ControlLabel>Password</ControlLabel>
<FormControl
type="password"
value={fields.password}
onChange={handleFieldChange}
/>
</FormGroup>
<LoaderButton
block
type="submit"
bsSize="large"
isLoading={isLoading}
disabled={!validateForm()}
>
Login
</LoaderButton>
</form>
</div>
);
}
You’ll notice that we are using our useFormFields
Hook. A good way to think about custom React Hooks is to simply replace the line where we use it, with the Hook code itself. So instead of this line:
const [fields, handleFieldChange] = useFormFields({
email: "",
password: ""
});
Simply imagine the code for the useFormFields
function instead!
Finally, we are setting our fields using the function our custom Hook is returning.
onChange={handleFieldChange}
Now we are ready to tackle our sign up page.
For help and discussion
Comments on this chapter