I recently had to refactor a React Form when working with multiple inputs and I thought I'd share how I did it.
This is how my form looks like:
The Problem
- Look at the starter code below
- This
Form
component has 5 input fields in total; 5 different states and 5 differentonChange
inline functions - This is not exactly DRY code π
import React, { useState } from "react";
export default function Form() {
const [newCompany, setCompany] = useState("");
const [newPosition, setPosition] = useState("");
const [newLink, setNewLink] = useState("");
const [newDate, setNewDate] = useState("");
const [newNote, setNewNote] = useState("");
return (
<form>
<input
value={newCompany}
onChange={(e) => setCompany(e.target.value)}
label="Company"
/>
<input
value={newPosition}
onChange={(e) => setPosition(e.target.value)}
label="Job Title"
/>
<input
value={newLink}
onChange={(e) => setNewLink(e.target.value)}
label="Job Link"
/>
<input
type="date"
value={newDate}
onChange={(e) => setNewDate(e.target.value)}
/>
<input
value={newNote}
onChange={(e) => setNewNote(e.target.value)}
label="Note"
/>
<button type="submit"> Submit </button>
</form>
);
}
- Also, if I want to add a
reset
function later, my code will look like this: π π½ββοΈ
const reset = () => {
setCompany("");
setPosition("");
setNewLink("");
setNewDate("");
setNewNote("");
};
The Solution: Refactoring β¨
Step 1: Add input default values and initialize state
- First, let's add default values to ALL input fields
- How do we do that? We create an object literal with those values and set to empty string
- Then, with the
useState()
React Hook we initialize ourvalues
state with theinitialValues
object -
Important: Remember to add the
value
attribute to every input field with its corresponding value (e.g.values={values.company}
)
const initialValues = {
company: "",
position: "",
link: "",
date: "",
note: "",
};
export default function Form() {
const [values, setValues] = useState(initialValues);
return (
<form>
<input
value={values.company}
onChange={(e) => setCompany(e.target.value)}
label="Company"
/>
//...
Step 2: Handle multiple input change
- The goal here is to handle ALL inputs with a single
onChange
handler - In order to update and keep track of our input fields every time they change, we need to create a
handleInputChange
function (see below) - What's happening here? (quick recap)
- First, we're using object destructuring to get or extract the
name
and thevalue
attributes from our inputs (look at the the comments below - they're equivalent) - Then, we're updating our
values
state object with the existing values by using thesetValues()
function and the spread operator - And finally, we're updating the
value
of the event that was triggered by thatonChange
with this ES6 syntax:[name]: value
- This is a very important step!
We need to add a
name
attribute to our inputs and[name]: value
here means that we're setting a dynamicname
property key (taken from our inputs - e.g.company: e.target.value
) which will be equal to thevalue
of our current input state.
- First, we're using object destructuring to get or extract the
Reference: React Docs
//...
const handleInputChange = (e) => {
//const name = e.target.name
//const value = e.target.value
const { name, value } = e.target;
setValues({
...values,
[name]: value,
});
};
return (
<form>
<input
value={values.company}
onChange={handleInputChange}
name="company" //IMPORTANT
label="Company"
/>
// ...
Step 3: Add handleInputChange
to input fields
- Add the
handleInputChange
function to theonChange
prop of every input field - Look at the final code; this is a much cleaner and manageable form ππ½
import React, { useState } from "react";
const initialValues = {
company: "",
position: "",
link: "",
date: "",
note: "",
};
export default function Form() {
const [values, setValues] = useState(initialValues);
const handleInputChange = (e) => {
const { name, value } = e.target;
setValues({
...values,
[name]: value,
});
};
return (
<form>
<input
value={values.company}
onChange={handleInputChange}
name="company"
label="Company"
/>
<input
value={values.position}
onChange={handleInputChange}
name="position"
label="Job Title"
/>
// ... Rest of the input fields
<button type="submit"> Submit </button>
</form>
);
}
I hope it was useful. All comments and feedback are welcome! π
Discussion (6)
Amazing perofrmance. Very thanks!
Really clean.
Amazing Deborah ! βΊοΈβΊοΈ
Thank you
This is beautiful!
Amazing perofrmance. Very thanks!