Eval is a built-in JavaScript method that allows develops to evaluate and run JavaScript syntax that has been wrapped in a string. It is one of the least used methods due to its security vulnerabilities.
Google Chrome,for example, blocks the usage of eval with following error
Douglas Crockford claims in his book JavaScript: The Good Parts that the security problem with eval is that code contained inside a string can be exploited in cross-site attacks, with the result that "eval is evil";
However, in React, Javascript UI library, eval can be used to securely set the state of several form input values with only one function. Given that React uses a virtual DOM to update the actual DOM, there are no security concerns to worry about.
Consider the following React component;
export function MyForm() {
const [firstName, setFirstName] = React.useState("");
const [lastName, setLastName] = React.useState("");
const [age, setAge] = React.useState("");
const [gender, setGender] = React.useState("");
const [location, setLocation] = React.useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log({ firstName, lastName, age, gender, location });
/*
{
age: "61",
firstName: "Brendan",
gender: "Male",
lastName: "Eich",
location: "Pitsburrg, PA"} */
//send payload to server or display on UI
};
return (
<form
onSubmit={handleSubmit}
style={{
display: "flex",
flexDirection: "column",
background: "#ccc",
gap: 10,
maxWidth: 500,
padding: "1rem",
}}
>
<h1 style={{ textAlign: "center" }}>Simple User Form</h1>
<div>
<label htmlFor="FirstName">First Name</label>
<input
type="text"
name="FirstName"
id="FirstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
</div>
<div>
<label htmlFor="LastName">Last Name</label>
<input
type="text"
name="LastName"
id="LastName"
className="input"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
</div>
<div>
<label htmlFor="Age">Age</label>
<input
type="number"
name="Age"
id="Age"
value={age}
onChange={(e) => setAge(e.target.value)}
/>
</div>
<div>
<label>Gender</label>
<label htmlFor="Male">
{" "}
<input
type="radio"
name="Gender"
id="Male"
value="Male"
onChange={(e) => setGender(e.target.value)}
/>
Male
</label>
<label htmlFor="Female">
{" "}
<input
type="radio"
name="Gender"
id="Female"
value="Female"
onChange={(e) => setGender(e.target.value)}
/>
Female
</label>
</div>
<div>
<label htmlFor="Location">Location</label>
<input
type="text"
name="Location"
id="Location"
value={location}
onChange={(e) => setLocation(e.target.value)}
/>
</div>
<button
style={{
padding: 10,
background: "skyblue",
borderRadius: 8,
border: "none",
margin: "0 auto",
width: "100%",
color: "white",
}}
type="submit"
>
Submit
</button>
<style jsx>{`
label{
display:block;
margin:5px
font-weight:600;
}
input[type="text"],input[type="number"]{
width:100%;
padding:8px;
margin:5px;
border-radius:5px
}
input[type="radio"]{
display:inline-block;
}
`}</style>
</form>
);
}
This is a straightforward form element that gathers user biodata, sets it to the appropriate state using the onChange event handler function, and dynamically binds values from state variables to the HTML inputs.
It works as expected
The drawback with inline onchange event handling is that it doesn't offer a mechanism to evaluate data or make the user interface interactive when the user triggers blur or focus events.
In order to correct this, we employ the eval method, a single onchange event handler the HTML input attribute name
, which we use to dynamically setState using the eval method, and ultimately get the input values.
NB name
, id
and data-*
are all valid HTML input attributes, however I personally prefer name
when using eval.
The name of the HTML atribute property that will be utilised should resemble the name of the setState function that is set as the state variable.
const [lastName,setLastName]=React.useState(“”)
// LastName will be appended to keyword set
<input
type="text"
name="LastName" // this one should match LastName in setLastName
id="LastName"
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
Our updated functional component should now look like this:
export function MyForm() {
const [firstName, setFirstName] = React.useState("");
const [lastName, setLastName] = React.useState("");
const [age, setAge] = React.useState("");
const [gender, setGender] = React.useState("");
const [location, setLocation] = React.useState("");
/**this function extracts the name attribute from target as well as
* its value then dynamically uses eval to set state of respective state variable */
const handleChange = (e) => {
// grab the value
const value = e.target.value;
// grab name attribute of element under change
const nameAttribute = e.target.name;
// concatenate set + name atrribute, i.e setAge, setLocation
// then evaluate the function using eval method
const setState = eval("set" + nameAttribute);
/**do extra validations before seting state*/
/** Finally we set state, IMPORTANT */
setState(value);
};
const handleSubmit = (e) => {
e.preventDefault();
console.log({ firstName, lastName, age, gender, location });
/*
{
age: "61",
firstName: "Brendan",
gender: "Male",
lastName: "Eich",
location: "Pitsburrg, PA"} */
//send payload to server
};
return (
<form
onSubmit={handleSubmit}
style={{
display: "flex",
flexDirection: "column",
background: "#ccc",
gap: 10,
maxWidth: 500,
padding: "1rem",
}}
>
<div>
<label htmlFor="FirstName">First Name</label>
<input
type="text"
name="FirstName"
id="FirstName"
value={firstName}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="LastName">Last Name</label>
<input
type="text"
name="LastName"
id="LastName"
value={lastName}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="Age">Age</label>
<input
type="text"
name="Age"
id="Age"
value={age}
onChange={handleChange}
/>
</div>
<p>Gender</p>
<div style={{ display: "flex", gap: 10 }}>
<label htmlFor="Male">
{" "}
<input
type="radio"
name="Gender"
id="Male"
value="Male"
onChange={handleChange}
/>
Male
</label>
<label htmlFor="Female">
{" "}
<input
type="radio"
name="Gender"
id="Female"
value="Female"
onChange={handleChange}
/>
Female
</label>
</div>
<div>
<label htmlFor="Location">Location</label>
<input
type="text"
name="Location"
id="Location"
value={location}
onChange={handleChange}
/>
</div>
<button
type="submit"
style={{
padding: 10,
background: "skyblue",
borderRadius: 8,
border: "none",
margin: "0 auto",
width: "100%",
color: "white",
}}
>
Submit
</button>
<style jsx>{`
label{
display:block;
margin:5px
font-weight:600;
}
input[type="text"],input[type="number"]{
width:100%;
padding:8px;
margin:5px;
border-radius:5px
}
input[type="radio"]{
display:inline-block;
}
`}</style>
</form>
);
}
You'll now see that there is now a single handleChange function that manages all form inputs.And it still works!
This function takes the name attribute, evaluates it to a setState function, and then captures the value of the element under change. Every input element can be validated adding blur events then using eval method inside blur event handlers. See how the eval approach simplifies the task at hand?
Top comments (0)