Best Practices to make you the best React dev you can be (Part 2)

Welcome to Part 2 of this series

  1. Avoiding useEffect as often as possible

The useEffect hook is used for side effects (data fetching, updating the DOM, subscribing to real-time events, etc). I'm sure you're aware of the second parameter of useEffect, the dependency array, which holds the state that useEffect will react to when that state changes. You might want certain code to run when that state is equal to a certain value, and if your first instinct is a useEffect, you'd want to look at things a different way. Let's say we have a state to track how many digits we've entered into an input.

const PinComponent = () => {
    const [pin, setPin] = useState("");

    useEffect(() => {
        if (pin.length > 4) {
            alert("PIN is already 4 digits")
        }
    }, [pin]);

    return (
        <input 
            value={pin} 
            onChange={(e) => setPin(e.target.value)} 
        />
    )
}

We can avoid the useEffect by checking the pin length inside the onChange function before a new digit is entered.

const PinComponent = () => {
    const [pin, setPin] = useState("");

    const handleChange = (e) => {
        if (pin.length > 4) {
            alert("PIN is already 4 digits")
        }
        setPin(e.target.value)
    }

    return (
        <input 
            value={pin} 
            onChange={handleClick} 
        />
    )
}

Also, note that whether the dependency array is empty or not, useEffect will run at least once (when your component is mounted). Keep that in mind to avoid unexpected results.

  1. Group elements that need certain state into components.

I'm sure you remember one of the fundamentals of React, when a state changes your component re-renders. We might have a big component, with lots of elements like a whole page with several state, and when a single state changes, this whole component re-renders and this not performant at all. Let's take a login page, for example. We have email, and password as state. Whenever the user enters any character in the email or password field, the whole login page re-renders.

import React, { useState } from "react";

const LoginPage = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [rememberMe, setRememberMe] = useState(false);

  const handleEmailChange = (e) => setEmail(e.target.value);
  const handlePasswordChange = (e) => setPassword(e.target.value);
  const handleRememberMeChange = () => setRememberMe(!rememberMe);

  const handleSubmit = (e) => {
    e.preventDefault();

    // Perform login logic here using 'email' and 'password'
    console.log("Logging in with:", email, password);
    console.log("Remember Me:", rememberMe);
  };

  return (
    <div>
      <h2>Login Form</h2>
      <form onSubmit={handleSubmit}>
        <label>
          Email:
          <input 
            type="email" 
            value={email} 
            onChange={handleEmailChange} 
            required 
           />
        </label>
        <br />
        <label>
          Password:
          <input 
            type="password" 
            value={password} 
            onChange={handlePasswordChange} 
            required 
           />
        </label>
        <br />
        <label>
          <input 
            type="checkbox" 
            checked={rememberMe} 
            onChange={handleRememberMeChange} 
          />
          Remember Me
        </label>
        <br />
        <button type="submit">Login</button>
      </form>
      <p>
        <a href="/forgot-password">Forgot Password?</a>
      </p>
    </div>
  );
};

export default LoginForm;

We can improve this by creating a separate component for the from fiels and then using it inside the LoginPage component. As used below.

import React, { useState } from "react";

const LoginForm = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [rememberMe, setRememberMe] = useState(false);

  const handleEmailChange = (e) => setEmail(e.target.value);
  const handlePasswordChange = (e) => setPassword(e.target.value);
  const handleRememberMeChange = () => setRememberMe(!rememberMe);

  const handleSubmit = (e) => {
    e.preventDefault();

    // Perform login logic here using 'email' and 'password'
    console.log("Logging in with:", email, password);
    console.log("Remember Me:", rememberMe);
  };

  return (
    <form onSubmit={handleSubmit}>
        <label>
          Email:
          <input 
            type="email" 
            value={email} 
            onChange={handleEmailChange} 
            required 
           />
        </label>
        <br />
        <label>
          Password:
          <input 
            type="password" 
            value={password} 
            onChange={handlePasswordChange} 
            required 
           />
        </label>
        <br />
        <label>
          <input 
            type="checkbox" 
            checked={rememberMe} 
            onChange={handleRememberMeChange} 
          />
          Remember Me
        </label>
        <br />
        <button type="submit">Login</button>
      </form>
  );
};

export default LoginForm;
const LoginPage = () => {

  return (
    <div>
      <h2>Login Form</h2>
      <LoginForm/>
      <p>
        <a href="/forgot-password">Forgot Password?</a>
      </p>
    </div>
  );
};

export default LoginForm;
  1. Using UI libraries unjudiciously

    It's not uncommon to get help from a UI library to build out UI components like buttons, inputs, and the likes. Creating UI components from scratch can be very time-consuming and distracting from the actual task at hand. I have a LinkedIn post on the best UI libraries here. But you should know how to use UI libraries judiciously because UI libraries add a significant amount of JavaScript to your JavaScript bundle (higher bundle size leads to slower initial page load). Don't install a UI library just because of a single UI component, neither should you use more than one UI library in your project. Look at every option you have and pick according to your needs. If the UI library you use does not have the UI component you seek, look for code samples of people online who have created such a component, or install an NPM package for such a component (still adds to your bundle size but not as much).

  2. Knowing when to not use React

    To be a better React developer, you should also know when not to use React. Yes, React is a great tool for building UI's on the web and it has great developer experience, but React should not be your go-to tool for everything. React just like every tool has its pros and cons, it's great for interactivity (although Solid.js beats it in this aspect), but it's bad for SEO by default, and SEO is a business-critical need. You might wonder how React is bad for SEO. React ships a lot of JavaScript to the browser by default, and that bundle slows down the initial page load of your webpage. Content rendered on the browser with React is not pure HTML, so it cannot be crawled by search engine bots making it not so good for Search Engine Optimization (SEO). If you need to build a landing page or a static website (where SEO is critical) that just displays information, your go-to should be plain HTML, CSS, and JS. If you feel like you need something more sophisticated or with better developer experience, use frameworks like Qwik or Astro that ship little or no JavaScript to the browser by default. But when you need to build a web app with highly interactive views, React (although Next.js is an even better choice) is your best bet.

That's all for today, please leave a like and let me know in the comments if you enjoyed this. You can also reach out to me on Twitter and LinkedIn. Until next time, see you later friends.