EngineerSpock

Неха Шарма – автор оригинала

ReactJS — одна из самых популярных библиотек JavaScript для создания масштабируемых и эффективных приложений.

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

В этой статье вы узнаете, как улучшить свой код React. Я поделюсь своими любимыми подсказками и покажу примеры кода, чтобы вы увидели, как все работает. Это поможет вам писать надежный, масштабируемый и читаемый код.

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

Лайкосы / Подписки / Курсы

1. Использование констант

В JavaScript мы можем объявлять константы, используя ключевое слово const. Это помогает нам избежать повторного объявления одного и того же значения. Таким образом, константы — отличный выбор для хранения API-ключей и других подобных значений.

Константы улучшают масштабируемость, читабельность и интернационализацию любой кодовой базы React.  

В каждом проекте ReactJS вам следует избегать жесткого кодирования строк (контента) в ваших компонентах.

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

Константы включают в себя:

  • API-ключи
  • URL-адреса
  • контент

Многие веб-сайты поддерживают несколько языков, например английский, французский, испанский и т. д. Это еще называется интернационализацией (или сокращенно i18n).

Если вы включаете функции i18n на своем сайте, то необходимо создать отдельные файлы констант для своего контента, например, en.js и fr.js. Даже если у вас нет поддержки нескольких языков или нет i18n, все равно рекомендуется хранить контент вне кода в файле констант.

Вы можете назвать файл констант как [LANGUAGE_INITIAL].js, так и constants.js. Это наиболее распространенные имена файлов, которые разработчики используют для данной цели.

Как создать файл констант

Константы — это просто объекты JavaScript с ключом/значениями. Мы начинаем с объявления объекта с именем, которое отражает содержащийся в нем контент. Поскольку это строки, мы заключаем их в кавычки. Перед экспортом сообщений выполните Object.freeze() — это позволит избежать случайного изменения значения вне какого-либо ключа.

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

// constants.js or en.js

const MESSAGES = {

    'HEADING': 'welcome to the website",

    'ENTER_YOUR_NAME': 'Enter user name',

    'HOME': [{

        'HEADING': 'welcome to the home'

     }]

}

Object.freeze(MESSAGES);

export default MESSAGES;

// Using constants.js in component

import MESSAGES from '../constants/constants

const Home = () => {

    return(

        <p>{MESSAGES.HEADING}</h1>

    )

}

export default Home;

2. Использование помощников/утилит

При работе над кодовой базой ReactJS крайне важно определить части кода, которые могут быть независимыми утилитами или помощниками, а не тесно связывать компоненты.

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

Зачем использовать помощники/утилиты?

Каждый компонент должен отвечать только за одно задание, что известно как «принцип разделения обязанностей».

Нам необходимо определить повторно используемые функции и переместить их в утилиты по следующим причинам.

  1. Это дает более чистые компоненты и более чистый код.
  2. Нет жесткой связи.
  3. Легко масштабируемые функциональные возможности.
  4. Простота обслуживания и отладки.
  5. Лучшее повторное использование.
  6. Компоненты теперь отвечают только за пользовательский интерфейс.

“`

// dateUtils.js : Moved the formatDate to a seprate util file to have reusability

export function formatDate(date) {

    const options = { year: 'numeric', month: 'long', day: 'numeric' };

    return new Date(date).toLocaleDateString(undefined, options);

}

// Updated Blog.jsx component after util

import React, { Component } from 'react';

import { formatDate } from './dateUtils';


const Blog = ({title, content, date}) => {

        return (

        <div>

            <h2>{title}</h2>

            <p>{content}</p>

            <p>Published on: {formatDate(date)}</p>

        </div>

    );

}

Например, в приведенном выше коде вы можете видеть, что у нас есть функция formatDate внутри компонента Blog. Здесь мы можем переместить formatDate в утилиты. Зачем?

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

3. Узнайте, как использовать свойства (Props)

Для связи между компонентами ReactJS мы используем свойства  (props). Но для этого имеются разные способы.

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

Давайте поговорим о том, как можно работать со свойствами в React.

Как использовать свойства (Props)

При использовании этого подхода нам приходится повторять свойства (props)каждый раз, когда мы их используем.

Такой подход не является лучшим способом использования свойств (props), поскольку мы повторяем свойства каждый раз, когда хотим их использовать. Помимо повторения, когда требуется создание вложенных свойств, потребуется вводить слишком много текста.

Ваше время как разработчика очень важно, и мы хотим оптимизировать код везде, где это возможно, и не повторять действия без крайней необходимости.

В приведенном ниже примере кода вы можете увидеть, почему этот подход не является оптимальным. У нас есть компонент Input с такими свойствами (props) как type, placeholder, name и changeHandler. В разделе return мы повторяем свойства (props) с каждым атрибутом (attribute), например, props.type.

const Input = (props) => {

    return <input

    type={props.type}

    placeholder={props.placeholder}

    name={props.name}

    className="block p-2 my-2 text-black"

    onChange={props.changeHandler}/>

}

export default Input;

Как выполнять деструктуризацию свойств (props)

Во втором способе работы со свойствами (props) мы используем деструктурирующее присваивание JavaScript.

Это улучшение по сравнению с первым подходом, поскольку здесь мы не будем повторять свойства (props) всякий раз, когда мы их используем.

Ниже приведен пример деструктуризации свойства (props). В первом фрагменте кода мы получаем type, placeholder, name и changeHandler из props в первую очередь в компоненте.

const { type, placeholder, name, changeHandler } = props

В приведенном ниже примере кода мы видим улучшение кода. У нас есть компонент Input с props, но вместо повторения props.name мы выполняем деструктуризацию свойств. Это огромное улучшение читабельности и простоты и скорости разработки.

const Input = (props) => {

    const { type, placeholder, name, changeHandler } = props;

    return <input

    type={type}

    placeholder={placeholder}

    name={name}

    className="block p-2 my-2 text-black"

    onChange={changeHandler}/>

}

Как выполнять деструктуризацию свойств в аргументах компонента

Это мой любимый метод деструктуризации свойств. Разработчики могут видеть в начале компонента, какие свойства будут использоваться в компоненте. У нас также нет повторения ключевых слов свойств (props).

По сравнению с последним подходом это

  • Соблюдение принципа DRY (не повторяйтесь): мы не повторяем свойства.
  • Удобочитаемость: в первой строке компонента (определении) мы знаем, какие свойства принимаются. Это улучшает читабельность и ясность компонента.

В приведенном ниже коде мы можем видеть, что мы деструктуризируем свойства (props) в определении компонента. Это улучшает читабельность. И теперь разработчики могут посмотреть на первую строку и понять, сколько и каких свойств (props) принимается в этом компоненте.

const Input = ({ type, placeholder, name, changeHandler }) => {

    return <input

    type={type}

    placeholder={placeholder}

    name={name}

    className="block p-2 my-2 text-black"

    onChange={changeHandler}/>

}

4. Наличие одного файла для каждого компонента

В ReactJS важно иметь по одному файлу для каждого компонента. Это помогает сделать код более чистым и надежным.

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

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

В приведенном ниже примере у нас есть один файл Input.jsx, который состоит из двух компонентов Input и Icon. Мы используем Icon в разделе return компонента Input.

Похоже, что Input и Icon связаны, и логично сгруппировать их в один файл. Но нам не следует этого делать, поскольку это не масштабируемое решение (и его нельзя использовать повторно).

// Don't do this:

import React from 'react';

// File name: Input.jsx

// This example shows how we are exporting 2 components from one file

// We should NOT do this

const Input = ({ type, placeholder, name, changeHandler }) => {

    return <>

    <input

    type={type}

    placeholder={placeholder}

    name={name}

    className="block p-2 my-2 text-black"

    onChange={changeHandler}/>




    <Icon type="warning"/>

}

        <>


const Icon = ({ type, url}) => {

    return <img src={url} data-type={type} />

}

export {Input, Icon};

Вместо этого нам следует создать два отдельных компонента для Input и Icon, как показано ниже. Это поможет вам повторно использовать оба компонента и масштабировать их по отдельности.

// Do this instead:

// Input.jsx: create 2 separate files for Input and InputIcon

import React from 'react';


const Input = ({ type, placeholder, name, changeHandler }) => {

    return <input

    type={type}

    placeholder={placeholder}

    name={name}

    className="block p-2 my-2 text-black"

    onChange={changeHandler}/>

}

export default Input;

5. Не используйте встраиваемые функции

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

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

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

В приведенном ниже фрагменте кода у нас есть функция handleIncrement, как встроенная функция для button.  Ее нельзя использовать повторно, и она и тесно связана с компонентом:

// Don't do this:

import React, { useState } from 'react';

function CounterInline() {

  const [count, setCount] = useState(0);

  const handleIncrement = () => {

    setCount(count + 1);

  };

  return (

    <div>

      <p>Count: {count}</p>

      <button onClick={handleIncrement}>Increment</button>

    </div>

  );

}

export default CounterInline;

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

В приведенном ниже коде мы можем использовать incrementCount для button, и для этой функции предполагается два аргумента. Здесь мы сделали эту функцию многократно используемой:

// Do this instead:

import React, { useState } from 'react';

// Standalone function for incrementing

function incrementCount(currentCount, setCount) {

  setCount(currentCount + 1);

}

const CounterStandalone = () => {

  const [count, setCount] = useState(0);

  return (

    <div>

      <p>Count: {count}</p>

      <button onClick={() => incrementCount(count, setCount)}>Increment</button>

    </div>

  );

}

export default CounterStandalone;

6. Реализация компонента 404 и маршрутизация

При реализации маршрутизации в ReactJS нам следует добавить компонент 404.

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

React-маршрутизатор предоставляет простой способ отображения ошибки, когда сервер возвращает 404.  Необходимо будет создать компонент, который должен отображаться, когда с сервера возвращается код состояния 404.

Каждый раз, когда пользователь вводит или достигает маршрута, который не найден, страница 404 показывает пользователю ошибку, что улучшает взаимодействие с пользователем (вместо того, чтобы просто видеть «404» без каких-либо объяснений).

Совет. В компоненте добавьте ссылку на домашнюю страницу вашего сайта. Это поможет пользователю перейти на главную страницу сайта.

 

<route path="*" component={<Error404/>} />

7. Постепенная выборка данных

В приложениях React вы часто будете выполнять выборку данных через API-интерфейсы.

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

Это улучшит производительность приложения, а также алгоритм взаимодействия с пользователем.

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

Пакет react-lazyload можно установить следующим образом:

// install react-lazyload package

npm install react-lazyload

Далее приведен код:

// Create a component - ItemList.jsx

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

import axios from 'axios';

const ItemList = () => {

  const [items, setItems] = useState([]);

  useEffect(() => {

    // Fetch data from your API here

    axios.get('https://example.com/api/items')

      .then(response => {

        setItems(response.data);

      })

      .catch(error => {

        console.error('Error fetching data:', error);

      });

  }, []);

  return (

    <div>

      <h2>Item List</h2>

      <ul>

        {items.map(item => (

          <li key={item.id}>

            {item.name}

          </li>

        ))}

      </ul>

    </div>

  );

};

export default ItemList;

// App.js

import React, { lazy, Suspense } from 'react';

import LazyLoad from 'react-lazyload';

// Import the lazy-loaded component

const ItemList = lazy(() => import('./ItemList'));

function App() {

  return (

    <div>

      <h1>React LazyLoad with API Data Example</h1>

      {/* Use React LazyLoad to lazy-load the component */}

      <LazyLoad height={200}>

        <Suspense fallback={<div>Loading...</div>}>

          <ItemList />

        </Suspense>

      </LazyLoad>

    </div>

  );

}

export default App;

В приведенном выше примере кода мы создали компонент, который будет выполнять запросы API-интерфейса и отображать данные API-интерфейса ItemList.jsx. При использовании ItemList в App, вместо отображения всех данных за один раз мы будем использовать LazLoad для загрузки компонента.

В результате компонент загружается по мере необходимости, когда он почти виден в области просмотра, и будут отображаться данные из API-интерфейса.

Некоторые из пакетов, которые вы можете использовать для отложенной загрузки, — это react-lazyload, react-infinite-scroll-component и react-paginate.

8. Использование уникальных значений для атрибутов ключа

Одна из причин популярности React — его «виртуальная DOM (объектная модель документа)».

Виртуальная DOM (VDOM) помогает оптимизировать процесс обновления пользовательского интерфейса.

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

React нужен атрибут key, чтобы определить, на каком узле произошло изменение. Вот почему мы всегда должны использовать уникальное значение key.

Хорошим примером того, как это сделать, является добавление id каждого элемента.

Примечание. Используйте индекс в качестве ключа только в том случае, если ваши данные статичны, а не переупорядочены или отфильтрованы.

// List.jsx

import React from 'react';

const List = ({ items }) => (

  return(<ul>

    {items.map((item, index) => (

      <li key={index}>{item}</li>

    ))}

  </ul>)

);

export default List;

// App.jsx

import List from './List';

const App = () => {

  const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4', 'Item 5'];

  return (

    <div>

      <h1>Simple List Example</h1>

      <List items={items} />

    </div>

  );

};

export default App;

В приведенном выше примере кода мы создаем список путем сопоставления данных. В компоненте List мы присваиваем index атрибуту key. Это будет использоваться в ReactJS с точки зрения внутренней структуры для оптимизации производительности всякий раз при обновлении или изменении какого-либо li.

9. Использование типов

Использование инструментов со встроенной статической проверкой типов (например, TypeScript) может помочь вам избежать ненужных ошибок в коде.

Такой инструмент также будет поддерживать ваш код, проверяя качество и тип.

Если вы только начинаете изучать проверку типов, вы можете начать с proptypes, а затем изучить TypeScript.

JavaScript не является строго типизированным, а это означает, что существует большая вероятность возникновения неожиданных ошибок или ошибок согласования типов.

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

Таким образом, строгие типы и TypeScript могут помочь вам избежать таких неожиданных ошибок во время разработки.

import React from 'react';

import PropTypes from 'prop-types';

const UserCard = ({ name, age, email }) => {

  return (

    <div>

      <h2>User Card</h2>

      <p>Name: {name}</p>

      <p>Age: {age}</p>

      <p>Email: {email}</p>

    </div>

  );

};

// Define the prop types for the UserCard component

UserCard.propTypes = {

  name: PropTypes.string.isRequired, // A required string prop

  age: PropTypes.number.isRequired,  // A required number prop

  email: PropTypes.string,           // An optional string prop

};

import UserCard from 'userCard';

const App = () => {

  return (

    <div>

      <h1>PropTypes Example</h1>

      <UserCard name="John Doe" age={30} email="john@example.com" />

    </div>

  );

};

export default App;

В приведенном выше примере кода мы создали компонент UserCard. Этот компонент принимает 3 свойства: имя, срок действия и адрес электронной почты. Используя proptypes, мы объявим для свойств два значения: тип данных (каким будет тип данных свойства, например строка, число и т. д.) и является ли он обязательным или необязательным.

Если при использовании компонента UserCard кто-то передает неправильный тип данных или пропускает требуемое свойство, код выдает ошибку и предупреждение о необходимости ее исправления.

10. Использование функций lazy()  и Suspense()

ReactJS использует сборщик Webpack (если вы используете create-react-app).

Webpack осуществляет объединение кода и выполняет такие функции, как встряска дерева вызовов.

React работает следующим образом: он загружает весь код на стороне клиента, даже если он нам не нужен. Это задача, требующая больших затрат. Если ваш пакет имеет большой размер, это повлияет на производительность ваших приложений.

Хороший способ избежать этого — отложенная загрузка кода по требованию с помощью lazy(), что позволит маршрутам загружаться тогда, когда они необходимы.

const LazyComponent = lazy(() => import('./LazyComponent'));

При использовании lazy() нам также следует использовать Suspense(), поскольку lazy() — это асинхронный способ загрузки компонентов.

Мы не хотим показывать пользователю пустой экран, пока наш маршрут не загрузится. Suspense() помогает, показывая сообщение во время загрузки компонента.

<Suspense fallback={<div>Loading...</div>}>

        {/* The LazyComponent will only be loaded when needed */}

        <LazyComponent />

 </Suspense>

Заключение

Уф, мы дошли до конца. Эти советы применимы не только к большим кодовым базам, но и к проектам любого размера.

В общих словах мы познакомились со следующими концепциями

  1. Соблюдение принципа DRY
  2. Соблюдение принципа разделения обязанностей
  3. Создание хорошего алгоритма взаимодействия с пользователем путем постепенной загрузки данных
  4. Улучшение читабельности
  5. Улучшение простоты и скорости разработки
  6. Исключение ошибок во время разработки
  7. Улучшение производительности

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *