The single ESLint rule that will make your code cleaner

The single ESLint rule that will make your code cleaner

ESLint is a tool that helps you keep your code clean and readable. It can be configured to enforce a set of rules that will help you write better code. In this article, I will show you how with a single rule of ESLint your code will be much cleaner and easier to read.

The problem

Let's say we have a React component that renders a TO-DO list. The component has a few buttons and a few states. It also has a few methods that are used to update the state. The component looks something like this:

import React, { useState } from 'react';

const TODO_STATUS = {
  PENDING: 'pending',
  DONE: 'done',
};

export const TodoList = () => {
  const [inputValue, setInputValue] = useState('');
  const [todos, setTodos] = useState([]);

  const addTodo = () => {
    setTodos([
      ...todos,
      { id: Date.now(), title: inputValue, status: TODO_STATUS.PENDING },
    ]);
    setInputValue('');
  };

  const removeTodo = (todo) => {
    setTodos(todos.filter((t) => t.id !== todo.id));
  };

  const markAsDone = (todo) => {
    setTodos(
      todos.map((t) => {
        if (t.id === todo.id) {
          return { ...t, status: TODO_STATUS.DONE };
        }
        return t;
      }),
    );
  };

  const markAsPending = (todo) => {
    setTodos(
      todos.map((t) => {
        if (t.id === todo.id) {
          return { ...t, status: TODO_STATUS.PENDING };
        }
        return t;
      }),
    );
  };

  return (
    <div>
      <h1>TODO List</h1>
      <div>
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
        />
        <button onClick={addTodo}>Add</button>
      </div>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <span>{todo.title}</span>
            <button onClick={() => removeTodo(todo)}>Remove</button>
            {todo.status === TODO_STATUS.PENDING ? (
              <button onClick={() => markAsDone(todo)}>Mark as done</button>
            ) : (
              <button onClick={() => markAsPending(todo)}>
                Mark as pending
              </button>
            )}
          </li>
        ))}
      </ul>
    </div>
  );
};

This component is not very complex, but it is not very easy to read either. The main problem is that it has a lot of methods that are used to update the state. These methods are not very long, but they are long enough to make the component hard to read. If we want to make this component easier to read, we need to refactor it.

The solution

Using the rule max-lines-per-function of ESLint, we can limit the number of lines of code in a function:

{
  "rules": {
    "max-lines-per-function": [
      "warn",
      { "max": 70, "skipBlankLines": true, "skipComments": true }
    ]
  }
}

This rule will warn us if a function has more than 70 lines of code and will remind us to refactor it before it gets too long. I also added the options skipBlankLines and skipComments to ignore blank lines and comments, which are not relevant to the length of the function and can help us to make the code more readable.

Live example

A possible refactoring of the component could be splitting the component into smaller components and moving the methods to a custom hook:

import React, { useCallback, useState } from 'react';

export const TodoList = () => {
  const { todos, addTodo, removeTodo, markAsPending, markAsDone } =
    useTodoList();

  return (
    <div>
      <h1>TODO List</h1>
      <TodoForm onSubmit={addTodo} />

      <ul>
        {todos.map((todo) => (
          <TodoItem
            key={todo.id}
            todo={todo}
            onRemove={() => removeTodo(todo)}
            onDone={() => markAsDone(todo)}
            onPending={() => markAsPending(todo, TODO_STATUS.PENDING)}
          />
        ))}
      </ul>
    </div>
  );
};

const TodoForm = ({ onSubmit }) => {
  const [inputValue, setInputValue] = useState('');

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

    onSubmit(inputValue);
    setInputValue('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />

      <button type="submit">Add</button>
    </form>
  );
};

const TodoItem = ({ todo, onRemove, onDone, onPending }) => (
  <li>
    <span>{todo.title}</span>

    <button onClick={onRemove}>Remove</button>

    {todo.status === TODO_STATUS.PENDING ? (
      <button onClick={onDone}>Mark as done</button>
    ) : (
      <button onClick={onPending}>Mark as pending</button>
    )}
  </li>
);

const TODO_STATUS = {
  PENDING: 'pending',
  DONE: 'done',
};

const useTodoList = () => {
  // ...
};

Although this file is slightly longer than the previous one, it is much easier to read, and it can be easily refactored into smaller files. This modular approach make sure everyone new to the project can easily understand the code and start contributing without having to spend hours trying to understand what is going on (and yes, that includes you in 6 months!).

My own experience

I have been using this rule for a while now, and I can say that it has the single most important rule that contributed to the readability of my code. I have been using it in all my projects, and I have never regretted it. I highly recommend you to try it out and see for yourself.

If you're interested in all the rules I use in my projects, you can check out my @neuledge/eslint-config and @neuledge/eslint-config-next packages. They are botn extended all the recommended rules from ESLint and Unicorn and provide good guidelines for better coding practices.

Simply get started by running:

npm install --save-dev @neuledge/eslint-config

and add the following to your .eslintrc.json:

{
  "extends": ["@neuledge"]
}

Enjoyed the read?

Don’t miss out on my next article! to stay updated on the latest insights.

Need advice on your startup?

I’m available for interesting web projects. Send me some details and let’s start working!

Hire me
avataravataravataravataravatar

Let’s stay in touch!

Join 50+ other developers who receive my thoughts, ideas, and favorite links.