...

пятница, 10 июля 2020 г.

[Перевод] React-разработка: 6 путей к профессиональному росту

Последние полгода я наблюдал за развитием начинающих React-программистов. За это время они прошли просто невероятный путь. Скорость их обучения, их жажда знаний, их стремление к совершенству — всё это произвело на меня сильнейшее впечатление.

Как им это удалось? Как они превратились из тех, кто не может без посторонней помощи открыть PR, в тех, к кому обращаются за помощью другие? Что сделало людей, просящих советов, людьми, охотно дающих советы другим? Как ученики превратились в учителей?

Я задал им эти вопросы. Вот что они на них ответили.

1. Используйте ESLint и TypeScript


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

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

Например, можно установить ESLint-плагин eslint-plugin-react-hooks. Этот плагин заметит проблему в следующем коде, с виду — совершенно нормальном, и сообщит нам о том, что мы нарушаем одно из правил использования хуков.

// мы нарушаем первое правило, используя хук в условии
  if (userName !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', userName);
    });
  }

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

Тот, кто хорошо владеет TypeScript, не только становится более искусным JavaScript-программистом, но и, в итоге, начинает писать более качественный React-код.

2. Как следует познакомьтесь с React-хуками


Хуки React, с момента их появления в феврале 2019 года, буквально захватили мир React-разработки. Хотя команда React говорит о том, что не стоит рефакторить старый код, переводя его на хуки, хуки в наше время встречаются буквально повсюду.

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

Вам нужен какой-то побочный эффект? Если так — тогда хук useEffect — это ваш лучший друг. Нужно организовать наблюдение за состоянием компонента и выполнять его повторный рендеринг при изменении состояния? Взгляните на useState. Нужно хранить и обновлять некие значения между рендерингами, но, при изменении этих значений, рендеринг не выполнять? А может, вам нужно знать о высоте или ширине DOM-элементов? Тогда ваш друг — это useRef.

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

useEffect(() => {
  document.title = `You clicked ${count} times`;
}); // запускается при каждом рендеринге

Это хук легко оптимизировать, сделав так, чтобы он запускался бы не при каждом рендеринге, а только при изменении переменной count. Делается это путём включения count в массив зависимостей:
useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // Эффект запускается только при изменении count

Вам должно быть удобно использовать самые распространённые хуки и создавать собственные хуки. Вы, кроме того, должны хорошо ориентироваться в вопросах рационального использования хуков. Например, знать о том, когда использование хуков вроде useEffect приводит к повторному рендерингу, а когда — нет.

3. Не оптимизируйте код слишком рано


Разговор о хуках ведёт нас к теме преждевременной оптимизации. Я слишком часто видел, как начинающие React-разработчики готовы лечь костьми ради того, чтобы сделать свой код как можно более быстрым. React-код чаще всего оптимизируют с использованием хуков useMemo и useCallback. Но их применение оправдано не всегда.

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

У некоторых разработчиков может появиться непреодолимое желание написать код, напоминающий тот, который показан ниже. Они могут подумать так: «Буду обновлять список шоколадных конфет только при изменении общего списка кондитерских изделий». Реализация этой идеи приведёт к появлению кода, перегруженного вспомогательными конструкциями, который сложно читать.

const Chocolate = (props) => {
  const chocolates = useMemo(
    () => props.candies.filter((candy) => candy.type === "chocolate"),
    [props.candies]
  );
  return (
    <>
      {chocolates.map((item) => (
        <p>{item.name}</p>
      ))}
    </>
  );
};

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

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

Не стоит повсюду использовать useCallback и useMemo только из-за того, что вы недавно где-то про них прочли. Не стоит оптимизировать всё подряд. Лучше подождать появления проблемы, а потом уже её решать. Не стоит решать несуществующих проблем.

4. Знайте о том, когда нужно создавать новые компоненты


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

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

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

const ListItems = () => {
  const items = React.useState([]);
  React.useEffect(() => {
    async function fetchItems() {
      await fetched = fetchItems();
      setItems(fetched);
    }
  });
return (
    <>
      {items.map((item) => (
        <div className="item-container">
          <img src={item.img} />
          <div className="name">{item.name}</div>
          <div className="author">{item.author}</div>
        </div>
      ))}
    </>
  );
};

Тут может возникнуть соблазн пойти по пути использования «контейнера». Следуя этому стремлению, мы придём к созданию двух компонентов:
const ListContainer = () => {
  const items = React.useState([]);
  React.useEffect(() => {
    async function fetchItems() {
      await fetched = fetchItems();
      setItems(fetched);
    }
  });
return <List items={items} />;
};
const List = (props) => {
  return (
    <>
      {props.items.map((item) => (
        <div className="item-container">
          <img src={item.img} />
          <div className="name">{item.name}</div>
          <div className="author">{item.author}</div>
        </div>
      ))}
    </>
  );
};

Но в подобной ситуации следует поступить иначе. А именно, нужно абстрагировать те части компонента, которые играют исключительно презентационную роль. В результате получатся два компонента — компонент List (список) и компонент Item (элемент):
const List = () => {
  const items = React.useState([]);
  React.useEffect(() => {
    async function fetchItems() {
      await fetched = fetchItems();
      setItems(fetched);
    }
  });
return (
    <>
      {items.map((item) => (
        <Item item={item} />
      ))}
    </>
  );
};
const Item = ({ item }) => {
  return (
    <div className="item-container">
      <img src={item.img} />
      <div className="name">{item.name}</div>
      <div className="author">{item.author}</div>
    </div>
  );
};

5. Уделите особое внимание тестированию


Уровень владения технологиями тестирования — это то, что отделяет джуниоров от сеньоров. Если вы не знакомы с методиками тестирования React-приложений, вы можете найти и изучить массу материалов об этом.

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

Вот, например, мой материал, посвящённый полному циклу тестирования фуллстек-приложения, основанного на React.

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


Тут мы затронем тему управления состоянием приложения в React. Существует масса технологий для решения этой задачи. Например, это redux, mobx, recoil, API context и много чего ещё. Их непросто даже перечислить.

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

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

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

Итоги


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

Когда я вспоминаю о коде, который я писал, когда был React-джуниором, на ум мне приходит одна вещь. Я писал переусложнённый код, который тяжело было понять. По мере того, как я набирался опыта, я стал замечать, как мой код становится проще. Чем проще код — тем лучше. В моём коде теперь легко разобраться, а понять его можно даже через полгода после его написания, когда оказывается, что в него вкралась ошибка, с которой нужно разобраться.

А какие пути к профессиональному росту React-программистов известны вам?

Let's block ads! (Why?)

Комментариев нет:

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