DEV Community

Cover image for Bending the JavaScript bad parts for good in React js.
Vincent Kipyegon
Vincent Kipyegon

Posted on • Edited on

Bending the JavaScript bad parts for good in React js.

The JavaScript community has designated certain syntax and features in the JavaScript language as the "bad parts of the language" because of their quirks, security concerns, or redundancy.
However, React, a JavaScript UI libraty, supports some of these quiky features and advocates declarative programming over imperative programming, the implementation of a virtual DOM, no separation of concerns, and a single source of truth. (state)
Here are some:

1. Double negation short hand.

Double negation is a Boolean constructor's double negation, indicated by !! (2 exclamation points), provides a shortcut for determining if a value is true or false. Eslint frowns upon double negation because it is redundant, and some people say it is unreadable.
In Javascript, there are 9 falsy values in javascript namely:

null, undefined, NaN,0,-0,(empty template strings),false, [] “”, '', (single and double empty string)

Usage:

const posts=[];
let username="";  // empty string is falsy value
username=prompt("Enter your user name")

// username will either be undefined or empty string if someone doesn’t add there name.
if(!!username)
console.log(`Welcome ${username}`)
else
console.log(`Name not  added`);
if(!!posts)
console.log(` ${posts.length} posts found`)
else
console.log(`There are no posts`)

// To negate a value use a third exclamation mark;

const isOnline=true;
if(!!!isOnline)
console.log("You are currently offline")
else
console.log("Your are currently online")

// in React, we use double negation in JSX as such;
const [posts,setPosts]=React.useState([]);

 // we’re expecting a user object from,say, backend  asynchronousrequest
const [user,setUser]=React.useState(null)


<ul>
{!!posts.length ? Posts.map(post=><li key={post.id}>{post.title}</li>)}
</ul>

<div>
<h5>{!!user ? user?.name : ""}</h5>
<p>{!!user ? user?.title : ""} </p>
</div>
Enter fullscreen mode Exit fullscreen mode

2. Console.hell

Console.log is a useful tool for tracing an application's logic flow, printing data to the console, and debugging web and node applications. I, like the majority of developers, cannot survive without it.
However, Console.log should not be used in production since you do not want your app to display payload or errors there. If you frequently neglect to delete or comment out console messages in your code, add the following code to your root file to hide console.log messages in production.

if(process.env.NODE_ENV !=="development") console.log=()=>{};

We are assigning a useless empty arrow function to the console.log method, it will never be invoked. Reusing built-in JavaScript methods, often known as monkey patching, is frowned upon by the JavaScript community, but it also obscures our mistakes. Using the console.info() or console.error() methods, you can highlight critical developer mistakes in live code.

3. Currying event handlers

In JavaScript, currying is the process through which a function that accepts few arguments yields a function that only accepts one argument. When managing events in React, currying is highly effective; it keeps the code clean, understandable, and minimises inline event handling logic.
Take a look at the functional component:

function NewsPosts() {
  const [posts, setPosts] = React.useState([]);
const [post, setPost] = React.useState(null);
  const handlePost = (post) => {setPost(post)};
  return (
    <div>
      <h3>Latest Post</h3>
      <ul>
        {posts.length > 0 &&
          posts.map((post) => (
            <li key={post.id} onClick={() => handlePost(post)}>
              {post.body}
            </li>
          ))}
      </ul>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

The component takes an array of posts, checks if elements are present in the array,renders them and uses a callback function to handle a click event on individual post item.
We can rewrite the function logic using currying as follows:

function NewsPosts() {
  const [posts, setPosts] = React.useState([]);
  const [post, setPost] = React.useState(null);

  const handlePost = (post) => {
    return (e) => {
      e.preventDefault();
      setPost(post);
    };
  };
  return (
    <div>
      <h3>Latest Post</h3>
      <ul>
        {posts.length > 0 &&
          posts.map((post) => (
            <li key={post.id} onClick={handlePost(post)}>
              {post.body}
            </li>
          ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The function handlePost is called directly during rendering but it is returned as a curry then invoked when a click event is fired. Don’t you think its a cleaner way of writing code?

4. Global This

With the advent of server side rendering utilising node.js, it is now important to determine if the programme is executing in a client-side or server-side context. By doing so, the application-crashing Node.js methods and window (browser) methods won't be mixed together.
Global this is a feature added to ES2019 to help identify the environment in which an application is running in order to prevent application failure.
To execute specific statements while determining whether you are executing in the browser or node environment;

if(globalThis.window)
console.log(`Running in the browser`)
else
console.log(`Running in node env`)
Enter fullscreen mode Exit fullscreen mode

5. Don’t be eval

Eval is a built-in JavaScript method that allows you to evaluate and run JavaScript syntax that has been wrapped in a string.
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"; for example,

eval("alert('You have been hacked'))

However, in React, 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;

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();
    //send payload to server
  };
  return (
    <form onSubmit={handleSubmit}  style={{
        display: "flex",
        flexDirection: "column",
        gap: 10,
        maxWidth: 500,
      }}>
      <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"
          value={lastName}
          onChange={(e) => setLastName(e.target.value)}
        />
      </div>
      <div>
        <label htmlFor="Age">Age</label>
        <input
          type="text"
          name="Age"
          id="Age"
          value={age}
          onChange={(e) => setAge(e.target.value)}
        />
      </div>
      <div>
      <label htmlFor="Male">
          {" "}
          <input
            type="radio"
            name="Gender"
            id="Male"
            value="Male"
            onChange={handleChange}
          />
        </label>

  <label htmlFor="Female">
          {" "}
          <input
            type="radio"
            name="Gender"
            id="Female"
            value="Female"
            onChange={handleChange}
          />
        </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 type="submit">Submit</button>
    </form>
  );
}

Enter fullscreen mode Exit fullscreen mode

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.

The drawback with inline onchange even 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. So many onchange inlin event handlers setting state directly is overwhelming. We need a single source of truth to handle our onchange 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 input values.
name , id and data-*attribute are all valid HTML input attributes, however I personally favour name attribute.

NB: The name of the HTML attribute 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(“”)

<input
          type="text"
          name="LastName" // this one should match LastName in setLastName
          id="LastName"
          value={lastName}
          onChange={(e) => setLastName(e.target.value)}
        />
Enter fullscreen mode Exit fullscreen mode

Updated function should now look like this:

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);
    // get element by attribute select
    const element = document.querySelector(`input[name="${nameAttribute}"]`);
    // validate locatiom
    if (nameAttribute === "Location") {
      if (e.target.value.trim().length > 0) setState(value);
      // give user feedback
     element?.classList.remove("red-border");
    } else {
      // show some error
     element?.classList.add("red-border");
    }
    // validate gender
    if (e.target.type === "radio") {
      // do necessary valdiation
    }
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    //send payload to server
  };
  return (
    <form onSubmit={handleSubmit} style={{
        display: "flex",
        flexDirection: "column",
        gap: 10,
        maxWidth: 500,
      }} >
      <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={{ width: "100%", background: "skyblue" , padding: 0.5,
            borderRadius: 5}} >Submit</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

You'll now see that there is now a single handleChange method that manages all form inputs. This function takes the name attribute from the target property of event object e.target.name, evaluates it to a setState function, and then captures the value e.target.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?

6. Is it mounted?

One perplexing aspect of the React useEffect hook is that it occasionally executes even if the component is not mounted at the moment. Performance problems may result from this if a component fetches data from a REST API and renders it even though the user is not currently browsing that part.

React use Effect hook executes once right after the initial render, while keeping an eye out for modifications to the dependency array, and while unmounting (clean up).To fix that, we declare a variable inside useEffect that checks if component is currently mounted.

const fetchData=()=>{
// get some data from the server
}

React.useEffect(()=>{
let mounted=true;

if(!mounted) return; // if mounted is false, return from the hook since it is not mounted
// if its mounted  perform your side effects
fetchData()

return()=>{
mounted=false;}
},[]) // you can add your dependencies to the array
Enter fullscreen mode Exit fullscreen mode

Top comments (0)