Home » Уроки, извлеченные при обновлении до React 18 в SonarQube

Уроки, извлеченные при обновлении до React 18 в SonarQube

СонарКуб интерфейс написан на Реагировать и недавно мы прошли процесс обновления с версии 17 до 18. Чтобы дать вам более полное представление, приложение также написано на Машинопись и использует Является с Библиотека тестирования React (RTL) для тестирования.

Мы хотели поделиться тремя наиболее важными проблемами, с которыми мы столкнулись, и уроками, которые мы извлекли в ходе обновления. Вкратце, это были:

  1. Некоторые типы TypeScript изменены.
  2. Библиотеку тестирования React также необходимо обновить.
  3. React 18 приносит кардинальные изменения

Давайте разберемся, что это значит и как мы с ними справились.

Примечание. Этот пост был написан в соавторстве с командой разработчиков интерфейса SonarQube в составе Дэвида Чо-Лера, Амбруаза Кристеа и Филиппа Перрена.

Изменения типа TypeScript

Руководство по обновлению React 18 указывает на то, что оба @types/react и @types/react-dom необходимо обновлять по мере обновления, и «наиболее заметным изменением является то, что children prop теперь необходимо указывать явно при определении реквизита».

Хорошая новость для этого обновления заключается в том, что Себастьян Зильберманниз основной команды React, поддерживает коллекция кодмодов которые помогают автоматически обновлять типы при обновлении с React 17.

Вы можете запустить codemod с помощью npx следующим образом:

npx types-react-codemod preset-18 ./src

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

Например, преобразование для вывода списка children prop явно примет компонент, который выглядит следующим образом:

MyComponent: React.ComponentType

и замените его на:

MyComponent: React.ComponentType>

Однако будьте осторожны: мы обнаружили, что codemod может вкладывать PropsWithChildren type, и ваш тип может выглядеть так:

MyComponent: React.ComponentType>>

Хотя это и не вредно, вам придется исправлять эти типы по мере их обнаружения.

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

interface ComponentProps {
  children: React.ReactNode;
}

interface Props extends ComponentProps {
  children: () => React.ReactNode;
}

С типами React 18 это больше не работает, и теперь вы должны опустить объявление children первый.

interface Props extends Omit  {
  children: () => React.ReactNode;
}

Новые типы также не допускают неявного any типы параметров для useCallback функция. Вам нужно будет явно объявить типы, например:

import { useCallback, MouseEvent } from 'react';

export function SubmitButton(props: ButtonProps) {
  const handleClick = useCallback((event: MouseEvent) => {
    event.preventDefault();
    // Do something else.
  }, [...]);
  return ;
}

Когда вы обновляетесь @types/react до версии 18, ожидайте увидеть несколько подобных проблем.

Обновление библиотеки тестирования React

Мы обнаружили, что многие из наших тестов, которые раньше проходили успешно, теперь не удались после обновления React и RTL. Было две категории неудач: выбор времени и призывы к act().

Поддельные таймеры

РТЛ использует setTimeout на определенную задержку при моделировании пользовательских событий, но это не очень хорошо сочетается с Поддельные таймеры Jest. Это приводило к зависанию тестов и сбою с тайм-аутом.

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

const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTime })

Отыгрывание

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

Помощники RTL используют act внутри компании, поэтому при добавлении дополнительного вызова в act изначально было допустимым обходным путем для подавления предупреждения, теперь оно приводило к сбою тестов. Удаление лишних звонков на act снова сдала анализы. Если вы по-прежнему получаете предупреждения, у Кента С. Доддса есть подробная статья на эту тему. что вызывает предупреждение act(…) и как его исправить в контексте РТЛ.

Реагируйте на 18 критических изменений

Самое большое изменение в React 18 находится в корне приложения. ReactDOM.render больше не поддерживается и его следует заменить на createRoot. Хотя на первый взгляд это кажется простым изменением, которое обеспечивает лучший способ управления корнем приложения React, на самом деле оно меняет то, как React визуализирует ваше приложение. Включены две новые функции: автоматическая пакетная обработка и новый параллельный рендеринг.

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

Однако автоматическое пакетирование включается немедленно. Автоматическая пакетная обработка — это улучшение производительности в React 18, позволяющее сократить количество рендерингов за счет сбора изменений состояния в одно обновление. Однако это может вызвать неожиданное поведение.

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

Мы поняли, что эта пакетная обработка включает в себя любой подконтекст выполнения в одной области! Это означает, что если у вас есть setStateзатем Promise, который также выполняет setState когда он разрешается/отклоняется, оба изменения состояния будут группироваться в конце области, если они происходят близко друг к другу (например, в тестах, где имитируемые запросы выполняются почти мгновенно).

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

class MyComponent extends React.Component {
  // ...

  fetchProjects = async () => {
    const { shouldFetch } = this.state;

    if (shouldFetch) {
      this.setState({ loading: true });
      const projects = await this.fetchProjects();
      this.setState({ loading: false, projects: projects });
    }
  }

  handleFetchProjectsClick = async () => {
    this.setState({ shouldFetch: true });
    await this.fetchProjects();
  }

  // ...
}

В React 17, когда handleFetchProjectsClick назывался, он устанавливал shouldFetch государство, чтобы trueтогда позвоните fetchProjects. В пределах fetchProjects тест на shouldFetch было бы true и данные были получены. Это связано с тем, что задача обновления состояния выполняется до fetchProjects обещание обрабатывается.

В React 18 с createRoot проекты не загружаются, поскольку обновление состояния отложено до конца handleFetchProjectsClickтак когда fetchProjects бежит shouldFetch все равно будет ложным.

Если вам нужно гарантировать выполнение кода после установки состояния, вы можете использовать форму обратного вызова setState или новый метод ReactDOM.flushSync() метод.

class MyComponent extends React.Component {
  // ...

  handleFetchProjectsClick = async () => {
    this.setState({ shouldFetch: true }, () => {
      await this.fetchProjects();
    });
  }

  // ...
}

Асинхронный рендеринг в тестах

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

Наши тесты использовали синхронные методы RTL для поиска этого контента на странице, например:

expect(screen.getByText('Content')).toBeInTheDocument();

Замена RTL синхронный getBy запросы с асинхронным await findBy запрос исправляет проблему. Например:

expect(await screen.findByText('Content')).toBeInTheDocument();

Используя findBy запрос использует waitFor под капотом, чтобы дать DOM время для обновления, если это не происходит немедленно.

Обновление прошло успешно

Пользовательский интерфейс SonarQube теперь успешно работает на React 18. Хотя некоторые проблемы, с которыми мы столкнулись, были связаны с необходимостью обновления набора тестов, другие были обнаружены, потому что у нас есть комплексный набор тестов как для модульных тестов для компонентов, так и для комплексных тестов. -завершить тестирование, чтобы избежать сбоев в работе, когда пришло время развертывания. Написание тестируемого кода — это часть написания адаптируемого кода, одно из свойств чистого кода.. Эти тесты выявили моменты, которые необходимо обновить, и дали нам уверенность в том, что, когда они пройдут, приложение будет готово.

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

2024-01-26 13:07:43


1706408389
#Уроки #извлеченные #при #обновлении #до #React #SonarQube

Read more:  Цены на энергоносители: пик цен на электроэнергию ожидается на следующей неделе

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.