Keep components readable, put handlers after jsx

βŒ›2 min read
  • hoisting
  • code-organization
  • React 18
An abstract birds & book picture which resembles ease of read

The problem

Ui devs are used to writing handlers and components.
Ideally those should be understandable at a first glance.
I'd argue that library hooks and jsx are the most important bits. Thus should be kept at the top and easily accessible.

The answer

Use function declaration syntax and its' hoisting functionality to move repetitive handler logic below jsx and library core functions invocations.

Write like this πŸ‘‘

import { ChangeEvent, FormEvent, Fragment, useId, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./App.css";

type UserData = {
  name: string;
  age: string;
  uuid: string;
};

function App() {
  const [users, setUsers] = useState([]);
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  // let's use React 18's useId hook;
  const nameInputId = useId();
  const ageInputId = useId();

  return (
    
      
{/* remember that 0 && true will return literally zero ("0") */} {/* is a renderable ReactNode unlike false!!! */} {/* let's coerce length to boolean to avoid it */} {!!users.length && (

saved users

    {users.map((dr) => (
  • {`${dr.name} - ${dr.age}`}{" "}
  • ))}
)}
); function handleSubmitUser(event: FormEvent) { event.preventDefault(); const newUser = { name, age, uuid: uuidv4() } satisfies UserData; setUsers(users.concat(newUser)); handleClearForm(); } function handleClearForm() { setName(""); setAge(""); } function handleRemoveUser(uuid: string) { setUsers(users.filter((user) => user.uuid !== uuid)); } function handleNameInputChange(e: ChangeEvent) { setName(e.target.value); } function handleAgeInputChange(e: ChangeEvent) { setAge(e.target.value); } } export default App;

instead of this πŸ₯ΊπŸ₯€β€οΈβ€πŸ©Ή

import { ChangeEvent, FormEvent, Fragment, useId, useState } from "react";
import { v4 as uuidv4 } from "uuid";
import "./App.css";

type UserData = {
  name: string;
  age: string;
  uuid: string;
};

function App() {
  const [users, setUsers] = useState([]);
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  // most likely when the component grows you will have to scroll
  // a bunch of lines to get to the jsx

  // let's use React 18's useId hook;
  const nameInputId = useId();
  const ageInputId = useId();

  function handleSubmitUser(event: FormEvent) {
    event.preventDefault();
    const newUser = { name, age, uuid: uuidv4() } satisfies UserData;
    setUsers(users.concat(newUser));
    handleClearForm();
  }

  function handleClearForm() {
    setName("");
    setAge("");
  }

  function handleRemoveUser(uuid: string) {
    setUsers(users.filter((user) => user.uuid !== uuid));
  }

  function handleNameInputChange(e: ChangeEvent) {
    setName(e.target.value);
  }

  function handleAgeInputChange(e: ChangeEvent) {
    setAge(e.target.value);
  }

  /* remember that 0 && true will return literally zero ("0") */
  /* is a renderable ReactNode unlike false!!! */
  /* let's coerce length to boolean to avoid it */

  return (
    
      
{!!users.length && (

saved users

    {users.map((dr) => (
  • {`${dr.name} - ${dr.age}`}{" "}
  • ))}
)}
); } export default App;

Summary

Javascript's hoisting feature doesn't get the love it deserves. Try out the strategy i've described. You might end up sticking with it πŸ˜….