Написание чистого и удобочитаемого кода необходимо для повышения его качества. Кроме того, чистый код легче тестировать. Нет причин не потратить пять лишних минут на рефакторинг кода, чтобы сделать его более удобным для чтения.

В этой статье рассматриваются шесть лучших практик React 2021 года, чтобы улучшить свой код. Речь пойдет о следующих пунктах:

  1. Используйте event.target.name для обработчиков событий. 

  2. Как избежать ручного биндинга обработчиков событий к this?

  3. Используйте хуки React для обновления состояния.

  4. Кэширование затратных операций с useMemo.

  5. Разделяйте функции на отдельные элементы для улучшения качества кода.

  6. Как создавать пользовательские хуки в React?

#1: Используйте поле name обработчика события

Когда у вас есть форма с одним полем ввода, вы напишете одну функцию onFirstInputChange для перехвата содержимого поля ввода.

Однако нужно ли писать десять обработчиков событий, если у вас есть форма с десятью полями ввода? Ответ - нет.

Мы можем установить свойство name для поля ввода и обращаться к нему из обработчика событий. Это значение позволяет нам использовать один обработчик ввода для событий onChange.

Вот текущая ситуация при использовании неоптимизированной формы с двумя полями ввода. Нам приходится определять обработчик события onChange для каждого отдельного элемента ввода формы. Этот шаблон создает много повторяющегося кода, который трудно поддерживать.

export default class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            item1: "",
            item2: "",
            items: "",
            errorMsg: ""
        };
        this.onFirstInputChange = this.onFirstInputChange.bind(this);
        this.onSecondInputChange = this.onSecondInputChange.bind(this);
    }
    onFirstInputChange(event) {
        const value = event.target.value;
        this.setState({
            item1: value
        });
    }
    onSecondInputChange(event) {
        const value = event.target.value;
        this.setState({
            item2: value
        });
    }
    render() {
        return (
            <div>
                <div className="input-section">
                    {this.state.errorMsg && (
                        <p className="error-msg">{this.state.errorMsg}</p>
                    )}
                    <input
                        type="text"
                        name="item1"
                        placeholder="Enter text"
                        value={this.state.item1}
                        onChange={this.onFirstInputChange}
                    />
                    <input
                        type="text"
                        name="item2"
                        placeholder="Enter more text"
                        value={this.state.item2}
                        onChange={this.onSecondInputChange}
                    />
                </div>
            </div>
      );
    }
}

Как вы можете видеть, каждая функция обработчика события обновляет состояние. При работе с несколькими элементами формы код становится очень запутанным, поскольку для каждого события приходится писать новую функцию.

Давайте решим эту ситуацию по-другому, используя поле name. Можно получить доступ к этому значению через свойство event.target.name. Теперь создадим одну функцию, которая будет обрабатывать оба события одновременно. Поэтому удалим обе функции onFirstInputChange и onSecondInputChange.

onInputChange = (event) => {
  const name = event.target.name;
  const value = event.target.value;
  this.setState({
    [name]: value
  });
};

Легко, верно? Конечно, часто требуется дополнительная проверка данных, которые вы сохраняете в своем состоянии. Используйте оператор switch, чтобы добавить пользовательские правила проверки для каждого отправленного значения.

#2: Избегайте ручного биндинга this

Скорее всего, вы знаете, что React не сохраняет биндинг this при присоединении обработчика к событиям onClick или onChange. Поэтому нам необходимо привязать this вручную. Для чего мы привязываем *this*? Мы хотим привязать this обработчика события к экземпляру компонента, чтобы не потерять контекст, когда мы передаем его в качестве обратного вызова.

Вот классический пример "биндинга", который происходит в конструкторе.

class Button extends Component {
  constructor(props) {
    super(props);
    this.state = { clicked: false };
    this.handleClick = this.handleClick.bind(this);
  }
  
  handleClick() {
    this.props.setState({ clicked: true });
  }
  
  render() {
    return <button onClick={this.handleClick}>Click me!</button>;
  );
}

Тем не менее, биндинг больше не нужен, поскольку команда create-react-app CLI использует плагин @babel/babel-plugin-transform-class-properties версии >=7 и плагин babel/plugin-proposal-class-properties версии <7.

Примечание: Вам необходимо изменить синтаксис обработчика событий на синтаксис стрелочной функции.

Ниже приведен пример синтаксиса стрелочной функции. Здесь нам не нужно писать дополнительный код в конструкторе, чтобы связать this.

class Button extends Component {
  constructor(props) {
    super(props);
    this.state = { clicked: false };
  }
  
  handleClick = () => this.setState({clicked: true });
  render() {
    return <button onClick={this.handleClick}>Click me!</button>;
  );
}

Это так просто, как только возможно! Вам не нужно беспокоиться о биндинге функций в конструкторе.

#3: Используйте хуки React для обновления состояния

Начиная с версии React 16.8.0, появилась возможность использовать методы состояния и жизненного цикла внутри функциональных компонентов с помощью хуков React. Другими словами, можно писать более читабельный код, который также намного проще поддерживать.

Для этого мы будем применять хук useState. Для тех, кто не знает, что такое хук и зачем его использовать - вот краткое определение из документации React.

Что такое хук? Хук - это специальная функция, которая позволяет вам "подключаться" к фичам React. Например, useState - это хук, который позволяет добавлять состояние React в компоненты функции.

Когда я могу использовать хук? Если вы пишете функциональный компонент и понимаете, что вам нужно добавить в него состояние, раньше вам приходилось преобразовывать его в класс. Теперь можно использовать хук внутри существующего функционального компонента.

Сначала давайте рассмотрим, как мы обновляем состояние с помощью хука setState.

this.setState({
    errorMsg: "",
    items: [item1, item2]
});

Давайте воспользуемся хуком useState. Нужно импортировать этот хук из библиотеки react. Теперь мы можем объявить новые переменные состояния и передать им начальное значение. Используя деструктуризацию, получим переменную для извлечения значения и переменную для установки значения (она является функцией). Давайте посмотрим, как это сделать для приведенного выше примера.

import React, { useState } from "react";

const App = () => {
  const [items, setIems] = useState([]);
  const [errorMsg, setErrorMsg] = useState("");
};

export default App;

Теперь можно напрямую обращаться к константам items и errorMsg в вашем компоненте.

Далее, можно обновить состояние внутри функции следующим образом:

import React, { useState } from "react";

const App = () => {
  const [items, setIems] = useState([]);
  const [errorMsg, setErrorMsg] = useState("");

  return (
    <form>
        <button onClick={() => setItems(["item A", "item B"])}>
          Set items
        </button>
    </form>
  );
};

export default App;

 Вот как можно использовать хуки состояний.

#4: Кэширование операций с большими накладными расходами при помощи useMemo

Мемоизация - это техника оптимизации для хранения результатов затратных операций. Другими словами, сортировка часто является затратной операцией, требующей большого количества обработки. Не хочется выполнять эту затратную функцию для каждого рендера страницы.

Поэтому можно использовать хук useMemo для запоминания результата при передаче тех же параметров в мемоизированную функцию. Хук useMemo принимает функцию и входные параметры для запоминания. React называет это массивом зависимостей. Каждое значение, на которое ссылается функция, также должно появиться в вашем массиве зависимостей.

Вот простой, абстрактный пример. Мы передаем два параметра a и b в затратную функцию. Поскольку функция использует оба параметра, добавляем их в массив зависимостей для нашего хука useMemo.

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

#5: Разделение функций на чистые функции для улучшения качества кода

В соответствии с общепринятой практикой React, вы должны разделить функции, которые не используют ваш компонент. Другими словами, это функция, которая не зависит от какого-либо состояния или хуков React.

Поэтому сортировка является отличным примером функции, которую вы можете извлечь как чистую.

Можно задаться вопросом, почему необходимо использовать функциональное программирование?

Функциональное программирование - это процесс создания программного обеспечения путем компоновки чистых функций, избегая общего для всех состояния, мутируемых данных и побочных эффектов. Чистые функции лучше читаются и легче тестируются. Поэтому они улучшают качество кода. - freeCodeCamp

Теперь давайте применим эту концепцию к компонентам React. Вот функция, которая может находиться как внутри компонента React, так и снаружи. Обе они являются функциями сравнения, которые вы можете передать затратной функции сортировки, принимающей два входных параметра. Поскольку здесь нет взаимодействия с состоянием, можно извлечь обе функции в виде чистых. Это позволяет нам поместить чистые функции в отдельный файл и при необходимости импортировать их в несколько мест.

function ascSort (a, b) {
  return a < b ? -1 : (b > a ? 1 : 0);
}

function descSort (a, b) {
  return b < a ? -1 : (a > b ? 1 : 0);
}

#6: Создание пользовательских хуков React

Мы узнали, как использовать хуки React useState и useMemo. Тем не менее, React позволяет вам определять собственные хуки, чтобы извлечь логику и сделать компоненты более читаемыми.

Можно задать пользовательские хуки React, начиная с ключевого слова use, как и все остальные хуки React. Это полезно, когда вы хотите разделить логику между различными функциями. Вместо того чтобы копировать функцию, мы можем определить логику в виде React хука и повторно использовать ее в других функциях.

Вот пример React-компонента, который обновляет состояние, когда размер экрана становится меньше 600 пикселей. Если это происходит, переменная isScreenSmall  устанавливается в true. В обратном случае переменная становится false. Для обнаружения изменения размера экрана мы используем событие resize из самого окна.

const LayoutComponent = () => {
  const [onSmallScreen, setOnSmallScreen] = useState(false);

  useEffect(() => {
    checkScreenSize();
    window.addEventListener("resize", checkScreenSize);
  }, []);

  let checkScreenSize = () => {
    setOnSmallScreen(window.innerWidth < 768);
  };

  return (
    <div className={`${onSmallScreen ? "small" : "large"}`}>
      <h1>Hello World!</h1>
    </div>
  );
};

Теперь мы хотим использовать ту же логику в другом компоненте, который полагается на переменную isScreenSmall. Вместо того чтобы дублировать код, давайте превратим этот пример в пользовательский хук React.

import { useState, useEffect } from "react";

const useSize = () => {
  const [isScreenSmall, setIsScreenSmall] = useState(false);

  let checkScreenSize = () => {
    setIsScreenSmall(window.innerWidth < 600);
  };
  useEffect(() => {
    checkScreenSize();
    window.addEventListener("resize", checkScreenSize);

    return () => window.removeEventListener("resize", checkScreenSize);
  }, []);

  return isScreenSmall;
};

export default useSize;

Мы можем создать пользовательский хук React, обернув логику в функцию use. В этом примере мы назвали пользовательский хук React useSize. Теперь можно импортировать хук useSize в любое место, где он нам понадобится.

В пользовательских хуках React нет ничего нового. Вы оборачиваете логику функцией и даете ей имя, начинающееся с use. Она действует как обычная функция. Однако, следуя правилу начинаться с "use", вы сообщаете всем, кто импортирует ее, что это хук. Более того, поскольку это хук, вы должны структурировать его таким образом, чтобы он соответствовал правилам хуков.

Вот как теперь выглядит наш компонент. Код стал намного чище!

import React from 'react'
import useSize from './useSize.js'

const LayoutComponent = () => {
  const onSmallScreen = useSize();

  return (
    <div className={`${onSmallScreen ? "small" : "large"}`}>
      <h1>Hello World!</h1>
    </div>
  );
}

Бонусный совет: Мониторинг фронтенда

Мониторинг фронтенда является обязательным, если вы хотите понять, что делают пользователи и как ваше приложение реагирует на эти запросы. OpenReplay - это инструмент мониторинга фронтенда, который воспроизводит все действия ваших пользователей и показывает, как ведет себя ваше веб-приложение при каждой проблеме. Он позволяет воспроизводить проблемы, агрегировать ошибки JS и отслеживать производительность вашего веб-приложения.

Начните отслеживать работу вашего веб-приложения бесплатно.

Вот и все! Эта статья научила вас шести способам улучшения читабельности и качества кода.


Материал подготовлен в рамках курса «React.js Developer».

Всех желающих приглашаем на бесплатный онлайн-интенсив «React-hooks».

React-hooks появились в React с версии 16.8, сегодня они используются уже повсеместно. За два вебинара мы разберемся, как работать с React-hooks, создадим компонент с использованием hooks, а также научимся делать кастомные hooks. Поработаем с react-testing-library и научимся тестировать компоненты и кастомные hooks. >> РЕГИСТРАЦИЯ

Комментарии (1)


  1. n0ne
    14.09.2021 18:33

    По поводу #2.
    Я бы не советовал просто менять биндинг на стрелочную. Всё зависит от того, сколько экземпляров компонента будет на странице.