Don't be Evil; using JavaScript eval method safely with React hooks
Tags: webdev, javascript, react, programming
Published: 2023-01-28 09:48:28

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


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/idkwb3pqs61jpk9zwz7t.png)

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;

```javascript
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


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rdxt1pxpcraeo0dzbj1h.png)

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.**


```javascript
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:

```javascript
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?