How I think when I write a React component. 🤔
Ioannis Potouridis
Posted on November 2, 2019
This is going to be a short demonstration on how I usually think when I write a React component.
So, let's say that I want to create a form component.
I don't care what fields the form will have at the moment.
import React from 'react';
function Form() {
return (
<form>
{/* */}
</form>
)
}
export default Form;
I want to add a firstName
field.
import React, { useState } from 'react';
function Form() {
const [firstName, setFirstName] = useState('');
const handleFirstNameChange = ({ target }) => {
setFirstName(target.value);
}
return (
<form>
<div>
<label htmlFor="firstName">First name</label>
<div>
<input
id="firstName"
onChange={handleFirstNameChange}
type="text"
value={firstName}
/>
</div>
</div>
</form>
)
}
export default Form;
Looking good. 😎
I want to add a lastName
field.
import React, { useState } from 'react';
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const handleFirstNameChange = ({ target }) => {
setFirstName(target.value);
}
const handleLastNameChange = ({ target }) => {
setLastName(target.value);
}
return (
<form>
<div>
<label htmlFor="firstName">First name</label>
<div>
<input
id="firstName"
onChange={handleFirstNameChange}
type="text"
value={firstName}
/>
</div>
</div>
<div>
<label htmlFor="lastName">Last name</label>
<div>
<input
id="lastName"
onChange={handleLastNameChange}
type="text"
value={lastName}
/>
</div>
</div>
</form>
)
}
export default Form;
Adding that second field was way easier.
I used my copy paste
powers.
I want to add an email
field.
I shall use my powers once again. 🐱🏍
import React, { useState } from 'react';
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const handleFirstNameChange = ({ target }) => {
setFirstName(target.value);
}
const handleLastNameChange = ({ target }) => {
setLastName(target.value);
}
const handleEmailChange = ({ target }) => {
setEmail(target.value);
}
return (
<form>
<div>
<label htmlFor="firstName">First name</label>
<div>
<input
id="firstName"
onChange={handleFirstNameChange}
type="text"
value={firstName}
/>
</div>
</div>
<div>
<label htmlFor="lastName">Last name</label>
<div>
<input
id="lastName"
onChange={handleLastNameChange}
type="text"
value={lastName}
/>
</div>
</div>
<div>
<label htmlFor="email">Email</label>
<div>
<input
id="email"
onChange={handleEmailChange}
type="email"
value={email}
/>
</div>
</div>
</form>
)
}
export default Form;
...
Then I want to add a password
field.
...
Then I want to add another field.
...
...
STOP! 🤚
Every new field translates into these three changes:
- Adding a state and set state action for the field
- Adding a new event handler for the input
- Adding the HTML for the field
It's time for me now to use my real powers.
I'll attempt to decrease the number of changes that occur.
I don't want to add a new event handler for every input.
The only thing that changes in every event handler is the action that gets called.
I'll pass that as an argument.
import React, { useState } from 'react';
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const handleChange = setStateAction => ({ target }) => {
setStateAction(target.value);
}
return (
<form>
<div>
<label htmlFor="firstName">First name</label>
<div>
<input
id="firstName"
onChange={handleChange(setFirstName)}
type="text"
value={firstName}
/>
</div>
</div>
<div>
<label htmlFor="lastName">Last name</label>
<div>
<input
id="lastName"
onChange={handleChange(setLastName)}
type="text"
value={lastName}
/>
</div>
</div>
<div>
<label htmlFor="email">Email</label>
<div>
<input
id="email"
onChange={handleChange(setEmail)}
type="email"
value={email}
/>
</div>
</div>
</form>
)
}
export default Form;
I'll try to add that password
field now.
import React, { useState } from 'react';
function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleChange = setStateAction => ({ target }) => {
setStateAction(target.value);
}
return (
<form>
<div>
<label htmlFor="firstName">First name</label>
<div>
<input
id="firstName"
onChange={handleChange(setFirstName)}
type="text"
value={firstName}
/>
</div>
</div>
<div>
<label htmlFor="lastName">Last name</label>
<div>
<input
id="lastName"
onChange={handleChange(setLastName)}
type="text"
value={lastName}
/>
</div>
</div>
<div>
<label htmlFor="email">Email</label>
<div>
<input
id="email"
onChange={handleChange(setEmail)}
type="email"
value={email}
/>
</div>
</div>
<div>
<label htmlFor="password">Password</label>
<div>
<input
id="password"
onChange={handleChange(setPassword)}
type="password"
value={password}
/>
</div>
</div>
</form>
)
}
export default Form;
OK, looking a bit better.
I think I can cross that out from the list.
- Adding a state and set state action for the field
Adding a new event handler for the input- Adding the HTML for the field
I don't want to add a new state and set state action for every field.
I'll update the event handler since I'll use one set state action.
I'll also add a name property to those inputs.
import React, { useState } from 'react';
function Form() {
const [values, setValues] = useState({});
const handleChange = ({ target }) => {
setValues(prev => ({ ...prev, [target.name]: target.value }));
}
return (
<form>
<div>
<label htmlFor="firstName">First name</label>
<div>
<input
id="firstName"
name="firstName"
onChange={handleChange}
type="text"
value={values.firstName || ''}
/>
</div>
</div>
<div>
<label htmlFor="lastName">Last name</label>
<div>
<input
id="lastName"
name="lastName"
onChange={handleChange}
type="text"
value={values.lastName || ''}
/>
</div>
</div>
<div>
<label htmlFor="email">Email</label>
<div>
<input
id="email"
name="email"
onChange={handleChange}
type="email"
value={values.email || ''}
/>
</div>
</div>
<div>
<label htmlFor="password">Password</label>
<div>
<input
id="password"
name="password"
onChange={handleChange}
type="password"
value={values.password || ''}
/>
</div>
</div>
</form>
)
}
export default Form;
OK, I'll cross that one out as well.
Adding a state and set state action for the fieldAdding a new event handler for the input- Adding the HTML for the field
This is me going berserk now.
import React, { useState } from 'react';
const fields = [
{
id: 'firstName',
label: 'First name',
name: 'firstName',
type: 'text'
},
{
id: 'lastName',
label: 'Last name',
name: 'lastName',
type: 'text'
},
{
id: 'email',
label: 'Email',
name: 'email',
type: 'email'
},
{
id: 'password',
label: 'Password',
name: 'password',
type: 'password'
}
];
function Form() {
const [values, setValues] = useState({});
const handleChange = ({ target }) => {
setValues(prev => ({ ...prev, [target.name]: target.value }));
}
return (
<form>
{fields.map(({ id, label, name, type }, index) => (
<div key={index}>
<label htmlFor={id}>{label}</label>
<div>
<input
id={id}
name={name}
onChange={handleChange}
type={type}
value={values[name] || ''}
/>
</div>
</div>
))}
</form>
)
}
export default Form;
Well, now when I want to add a field, I just add one in my fields array. 😁
Adding a state and set state action for the fieldAdding a new event handler for the inputAdding the HTML for the field
What do you think?
Posted on November 2, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 28, 2024