4
\$\begingroup\$

I was working on a react hooks project.

I was making a form

In the form, I noticed myself writing a very wet code

Here is my code

const FormInvite = (props) => {
 const [firstName, setFirstName] = useState("");
 const [lastName, setLastName] = useState("");
 const [country, setCountry] = useState("")
 const [email, setEmai] = useState("")
 const changeSelectedCountry = (event) => {
 setCountry(event.target.value)
 }
 const changeFirstName = (event) => {
 setFirstName(event.target.value)
 }
 const changeLastName = (event) => {
 setLastName(event.target.value)
 }
 const changeEmail = (event) => {
 setEmail(event.target.value)
 }

Can someone help me/give recommendation so that I don't write those one liner, kind of representative code?

Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Jun 10, 2020 at 14:18
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

Given your form fields use appropriate names (i.e. field names match state properties), and you don't mind using a single state object you can create a single change handler that merges in each field update (similar to how a class-based component's this.setState would handle it) using the field name as the key.

const FormInvite = (props) => {
 const [state, setState] = useState({
 firstName: '',
 lastName: '',
 country: '',
 email: '',
 });
 const changeHandler = e => {
 const { name, value} = e.target;
 setState(state => ({ ...state, [name]: value }));
 }
 ...

const rootElement = document.getElementById("root");
const FormInvite = props => {
 const [state, setState] = React.useState({
 firstName: "",
 lastName: "",
 country: "",
 email: ""
 });
 const changeHandler = e => {
 const { name, value } = e.target;
 setState(state => ({ ...state, [name]: value }));
 };
 React.useEffect(() => {
 console.log('firstname changed', state.firstName);
 }, [state.firstName]);
 React.useEffect(() => {
 console.log('lastname changed', state.lastName);
 }, [state.lastName]);
 React.useEffect(() => {
 console.log('country changed', state.country);
 }, [state.country]);
 React.useEffect(() => {
 console.log('email changed', state.email);
 }, [state.email]);
 return (
 <form>
 <label>
 First
 <input
 type="text"
 value={state.firstName}
 name="firstName"
 onChange={changeHandler}
 />
 </label>
 <label>
 Last
 <input
 type="text"
 value={state.lastName}
 name="lastName"
 onChange={changeHandler}
 />
 </label>
 <label>
 Country
 <input
 type="text"
 value={state.country}
 name="country"
 onChange={changeHandler}
 />
 </label>
 <label>
 email
 <input
 type="email"
 value={state.email}
 name="email"
 onChange={changeHandler}
 />
 </label>
 </form>
 );
};
ReactDOM.render(
 <React.StrictMode>
 <FormInvite />
 </React.StrictMode>,
 rootElement
);
input {
 width: 3rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<body>
 <div id="root"></div>
</body>

NOTE: This uses a functional state update to ensure/preserve previous state values that aren't currently being updated.

Alternatively you can create a mapping of state => setState function and use a similar change handler as before, this time though you won't need to merge in updates manually.

const FormInvite = props => {
 const [firstName, setFirstName] = React.useState("");
 const [lastName, setLastName] = React.useState("");
 const [country, setCountry] = React.useState("");
 const [email, setEmail] = React.useState("");
 const fieldSetStateMap = {
 firstName: setFirstName,
 lastName: setLastName,
 country: setCountry,
 email: setEmail
 };
 const changeHandler = e => {
 const { name, value } = e.target;
 fieldSetStateMap[name](value);
 };
 ...

const rootElement = document.getElementById("root");
const FormInvite = props => {
 const [firstName, setFirstName] = React.useState("");
 const [lastName, setLastName] = React.useState("");
 const [country, setCountry] = React.useState("");
 const [email, setEmail] = React.useState("");
 const fieldSetStateMap = {
 firstName: setFirstName,
 lastName: setLastName,
 country: setCountry,
 email: setEmail
 };
 const changeHandler = e => {
 const { name, value } = e.target;
 fieldSetStateMap[name](value);
 };
 React.useEffect(() => {
 console.log("firstname changed", firstName);
 }, [firstName]);
 React.useEffect(() => {
 console.log("lastname changed", lastName);
 }, [lastName]);
 React.useEffect(() => {
 console.log("country changed", country);
 }, [country]);
 React.useEffect(() => {
 console.log("email changed", email);
 }, [email]);
 return (
 <form>
 <label>
 First
 <input
 type="text"
 value={firstName}
 name="firstName"
 onChange={changeHandler}
 />
 </label>
 <label>
 Last
 <input
 type="text"
 value={lastName}
 name="lastName"
 onChange={changeHandler}
 />
 </label>
 <label>
 Country
 <input
 type="text"
 value={country}
 name="country"
 onChange={changeHandler}
 />
 </label>
 <label>
 email
 <input
 type="email"
 value={email}
 name="email"
 onChange={changeHandler}
 />
 </label>
 </form>
 );
};
ReactDOM.render(
 <React.StrictMode>
 <FormInvite />
 </React.StrictMode>,
 rootElement
);
input {
 width: 3rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<body>
 <div id="root"></div>
</body>

NOTE: This can use a plain state update as each "piece of state" is fully independent of other state and the value is replaced each onChange.

answered Jun 11, 2020 at 4:51
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.