2022-07-19 10:12:15 +01:00
|
|
|
---
|
2022-09-06 15:44:40 +01:00
|
|
|
categories:
|
|
|
|
- Programming Languages
|
2022-07-19 10:12:15 +01:00
|
|
|
tags:
|
|
|
|
- javascript
|
|
|
|
- react
|
|
|
|
- react-hooks
|
|
|
|
---
|
|
|
|
|
|
|
|
# Forms using hooks
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
With hooks, form processing is exactly the same as
|
|
|
|
[classes](/Programming_Languages/React/Classes/Forms.md) in terms of the overall
|
|
|
|
methodology, but the syntax is slightly different as a result of the `useState`
|
|
|
|
hook.
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
## Basic approach
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Instead of using `this.state` and `this.setState` . We just have the `useState`
|
|
|
|
hook. But the controlled component principle is the same. Let's say we have a
|
|
|
|
simple email input:
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
```jsx
|
2024-02-02 15:58:13 +00:00
|
|
|
const [email, setEmail] = useState("");
|
2022-07-19 10:12:15 +01:00
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
As this is a form, the state change is going to be the result of user input. So
|
|
|
|
we need to prep our form to enable this.
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
```html
|
|
|
|
<input type="text" value="{email}" onChange="{setEmail}" />
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Now we just need to make good on the `setEmail` method we declared when we
|
|
|
|
initialised the state:
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
```jsx
|
|
|
|
const handleChange = (event) => {
|
|
|
|
setEmail(event.target.value);
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
### Applied example
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Here is an applied example of the above approach for a form that has three input
|
|
|
|
fields. This component outputs the inputs as JSON on submit:
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
```jsx
|
|
|
|
|
|
|
|
function FormHook() {
|
|
|
|
const [email, setEmail] = useState("");
|
|
|
|
const [phone, setPhone] = useState("");
|
|
|
|
const [age, setAge] = useState("");
|
|
|
|
const [formOutput, setFormOutput] = useState("Form output");
|
|
|
|
|
|
|
|
const handleSubmit = (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
setFormOutput(
|
|
|
|
JSON.stringify({ email: email, phone: phone, age: age }, null, 2)
|
|
|
|
);
|
|
|
|
};
|
|
|
|
return (
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
|
|
<input type="text" value={email} onChange={(event) => setEmail(event.target.value)}>
|
|
|
|
<input type="text" value={phone} onChange={(event) => setPhone(event.target.value)}>
|
|
|
|
<input type="number" value={age} onChange={(event) => setAge(event.target.value)}>
|
|
|
|
<button type="submit">Submit</button>
|
|
|
|
</form>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
## More complex forms
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
The above is fine if you only have one form with a couple of inputs. But if you
|
|
|
|
are managing multiple forms or forms with a complex array of inputs, you would
|
|
|
|
need to create `useState` declaration for every single input with a custom
|
|
|
|
`onChange` event for each one which is repetitious and not very clean.
|
2022-07-19 10:12:15 +01:00
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
So instead of this, just like with class-based controlled components, we use the
|
|
|
|
`name` HTML attribute to distinguish each input and create a generic `onChange`
|
|
|
|
function that distinguishes each separate input by destructuring a key, value
|
|
|
|
object using the `name`.
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
```jsx
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
|
|
<input type="text" name="email" value={formValues.email} onChange={handleChange}>
|
|
|
|
<input type="text" name="phone" value={formValues.phone} onChange={handleChange}>
|
|
|
|
<input type="number" name="age" value={formValues.age} onChange={handleChange}>
|
|
|
|
<button type="submit">Submit</button>
|
|
|
|
</form>
|
|
|
|
```
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
const initialState = {
|
2024-02-02 15:58:13 +00:00
|
|
|
email: "",
|
|
|
|
phone: "",
|
|
|
|
age: "",
|
2022-07-19 10:12:15 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
const [formValues, setFormValues] = useState(initialState);
|
|
|
|
|
|
|
|
const handleChange = (event) => {
|
2024-02-02 15:58:13 +00:00
|
|
|
const { name, value } = event.target;
|
|
|
|
setFormValues({ ...formValues, [name]: value });
|
2022-07-19 10:12:15 +01:00
|
|
|
};
|
|
|
|
```
|
|
|
|
|
|
|
|
There are three parts:
|
|
|
|
|
|
|
|
1. First we create the initial state.
|
2024-02-02 15:58:13 +00:00
|
|
|
2. Next we store this initial state as the variable in the `useState`
|
|
|
|
initialisation: `formValues` . We also provide a method `setFormValues` which
|
|
|
|
will be used by the change handler to log the user's inputs.
|
|
|
|
3. Finally we create the function that will log the user changes. First we use
|
|
|
|
object destructuring on the change event to enable us to retrieve the `name`
|
|
|
|
and `value` attributes of the HTML inputs in the component. Then we use
|
|
|
|
spread syntax to say that for each input pair, retrieve its value, using the
|
|
|
|
destructured `name` variable as the key.
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
### Applied example
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Below I have updated the previous context to this time reflect the new,
|
|
|
|
abstracted logic:
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
```jsx
|
|
|
|
function FormHookAbstracted() {
|
|
|
|
const initialState = {
|
|
|
|
email: "",
|
|
|
|
phone: "",
|
|
|
|
age: "",
|
|
|
|
};
|
|
|
|
const [formValues, setFormValues] = useState(initialState);
|
|
|
|
const handleChange = (event) => {
|
|
|
|
const { name, value } = event.target;
|
|
|
|
setFormValues({ ...formValues, [name]: value });
|
|
|
|
};
|
|
|
|
const handleSubmit = (event) => {
|
|
|
|
event.preventDefault();
|
|
|
|
setFormOutput(
|
|
|
|
JSON.stringify({ email: email, phone: phone, age: age }, null, 2)
|
|
|
|
);
|
|
|
|
};
|
|
|
|
return (
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
|
|
<input type="text" name="email" value={formValues.email} onChange={handleChange}>
|
|
|
|
<input type="text" name="phone" value={formValues.phone} onChange={handleChange}>
|
|
|
|
<input type="number" name="age" value={formValues.age} onChange={handleChange}>
|
|
|
|
<button type="submit">Submit</button>
|
|
|
|
</form>
|
|
|
|
)
|
|
|
|
};
|
|
|
|
export default FormHookAbstracted;
|
|
|
|
```
|
|
|
|
|
2024-02-02 15:58:13 +00:00
|
|
|
Note that instead of individual variables `email` , `phone`, `age` , this
|
|
|
|
approach returns a single object `formValues` . We could therefore access the
|
|
|
|
individual values with e.g `[formValues.email](http://formvalues.email)` .
|
2022-07-19 10:12:15 +01:00
|
|
|
|
|
|
|
As it is an object, it makes resetting to the original state very easy, viz:
|
|
|
|
|
|
|
|
```jsx
|
|
|
|
const handleReset = () => {
|
|
|
|
Object.values(formValues).map((x) => setFormValues(initialState));
|
|
|
|
};
|
|
|
|
```
|