React Query за 10 минут
Видео (рус): https://www.youtube.com/watch?v=4-SOv7eTfoQ
Действительно хорошее введение в React Query. За 11 минут пишем простое React-приложение, которое демонстрирует основные возможности и концепции этой библиотеки. Это будет табличка с данными, к которой затем еще прикрутится пагинация и форма создания нового элемента.
Видео объясняет, как:
- получить данные через
- как получить состояние запроса (isLoading, error)
- как работает кеширование и как оно помогает улучшить пользовательский опыт
- зачем React Query делает фоновые запросы
- как обновить данные на сервере через
- как принудительно сбросить кеш запроса (инвалидация кеша)
React Query умеет все то же самое, что и RTK Query, но работает по-другому: в форме хуков, без общего стора. С RTK Query мы выносили все, что связано с api в отдельный слайс, в отдельный файл и работали с этими данными отдельно. React Query же работает как хук, внутри компонента и предоставляет все необходимое на месте.
По первому впечатлению: React Query попроще, полегче, поприятнее (обожаю хуки), чем RTK Query, но возможно не потянет более сложные задачи, код станет запутанным. RTK Query тесно работает со стором и это дает больше возможностей.
#управлениесостоянием
Видео (рус): https://www.youtube.com/watch?v=4-SOv7eTfoQ
Действительно хорошее введение в React Query. За 11 минут пишем простое React-приложение, которое демонстрирует основные возможности и концепции этой библиотеки. Это будет табличка с данными, к которой затем еще прикрутится пагинация и форма создания нового элемента.
Видео объясняет, как:
- получить данные через
useQuery(key, fetchFn)
, в том числе как сделать запрос с параметрами (например, с номером текущей страницы)- как получить состояние запроса (isLoading, error)
- как работает кеширование и как оно помогает улучшить пользовательский опыт
- зачем React Query делает фоновые запросы
- как обновить данные на сервере через
useMutation(mutationFn, config)
- как принудительно сбросить кеш запроса (инвалидация кеша)
React Query умеет все то же самое, что и RTK Query, но работает по-другому: в форме хуков, без общего стора. С RTK Query мы выносили все, что связано с api в отдельный слайс, в отдельный файл и работали с этими данными отдельно. React Query же работает как хук, внутри компонента и предоставляет все необходимое на месте.
По первому впечатлению: React Query попроще, полегче, поприятнее (обожаю хуки), чем RTK Query, но возможно не потянет более сложные задачи, код станет запутанным. RTK Query тесно работает со стором и это дает больше возможностей.
#управлениесостоянием
YouTube
React Query за 10 минут! Быстрый курс
В этом ролике я расскажу о том, что такое React Query, как делать запрос на бэкенд, пагинацию, отправку POST запроса (мутации) и расскажу про плюсы и минусы этой библиотеки. Всё это за 11 минут!
⏰ Таймкоды:
00:00:00 Начало
00:00:46 Пишем код с помощью Axios…
⏰ Таймкоды:
00:00:00 Начало
00:00:46 Пишем код с помощью Axios…
👍4
Что вам больше нравится?
Anonymous Poll
15%
React Query
13%
RTK Query
21%
Не могу сравнить, работал только с одним инструментом
29%
Не могу оценить, не работал ни с одним инструментом
12%
Оба хороши, каждый для своей задачи
4%
Оба хороши и взаимозаменяемы
4%
Ни то, ни то не нравится
2%
Что-то другое :)
👍2
Forwarded from Cat in Web
Решаем задачки
Если интересно порешать задачки, то заходите 🤓:
- TypeScript Challenge
- Leetcode Challenge
#challenges #реклама
Если интересно порешать задачки, то заходите 🤓:
- TypeScript Challenge
- Leetcode Challenge
#challenges #реклама
Telegram
Leetcode Challenge
Пытаюсь решать задачки с leetcode
🔥2
React Query vs RTK Query: всестороннее сравнение
Статья (англ.): https://www.frontendmag.com/insights/react-query-vs-rtk-query/
В статье сравниваются два популярных решения для взаимодействия React-приложений с удаленными источниками данных.
Сходство
🔹Оба инструмента используют лучшие performance-практики работы с удаленными данными:
- кеширование запросов
- предотвращение дублирующихся запросов
- повторные запросы в случае ошибок
🔹Оба инструмента предлагают решения для самых распространненных задач получения данных:
- пагинация - обновление списка данных
- оптимистические обновления
Отличие
Отличие заключается в архитектуре инструментов.
🔸React Query - это независимая библиотека. Ее функциональность реализована в виде хуков, то есть привязана к жизненному циклу React-компонентов.
🔸RTK Query - это инструмент, созданный поверх Redux Toolkit. Он работает в парадигме Redux: через редьюсеры и экшены, взаимодействует с глобальным стейтом приложения.
Кривая обучения
Автор считает, что и React Query, и RTK Query интуитивно понятны. Это довольно спорное утверждение, так как чтобы понять и то, и другое, требуется немного отформатировать свое понимание работы с данными.
Что касается RTK Query, тут вообще требуется сначала разобраться с Redux Toolkit - а это дело непростое.
Когда что использовать (привет, кэп)
RTK Query лучше использовать, если:
- В приложении уже используется Redux и Redux Toolkit. Тогда RTK Query доступна из коробки и уже интегрирована с прочими инструментами.
- Требуется сложная логика управления данными. RTK Query - это более мощный инструмент и на нем можно построить более сложную систему.
React Query лучше использовать, если:
- В приложении не используется глобальное состояние или запросы данных с ним не взаимодействуют. React Query не предоставляет никаких способов создания глобального стейта или взаимодействия с ним, все только внутри компонента.
- Требуется простая логика обработки данных.
- Не хочется долго разбираться.
- Не хочется тянуть зависимости в виде Redux Toolkit.
#управлениесостоянием #redux
Статья (англ.): https://www.frontendmag.com/insights/react-query-vs-rtk-query/
В статье сравниваются два популярных решения для взаимодействия React-приложений с удаленными источниками данных.
Сходство
🔹Оба инструмента используют лучшие performance-практики работы с удаленными данными:
- кеширование запросов
- предотвращение дублирующихся запросов
- повторные запросы в случае ошибок
🔹Оба инструмента предлагают решения для самых распространненных задач получения данных:
- пагинация - обновление списка данных
- оптимистические обновления
Отличие
Отличие заключается в архитектуре инструментов.
🔸React Query - это независимая библиотека. Ее функциональность реализована в виде хуков, то есть привязана к жизненному циклу React-компонентов.
🔸RTK Query - это инструмент, созданный поверх Redux Toolkit. Он работает в парадигме Redux: через редьюсеры и экшены, взаимодействует с глобальным стейтом приложения.
Кривая обучения
Автор считает, что и React Query, и RTK Query интуитивно понятны. Это довольно спорное утверждение, так как чтобы понять и то, и другое, требуется немного отформатировать свое понимание работы с данными.
Что касается RTK Query, тут вообще требуется сначала разобраться с Redux Toolkit - а это дело непростое.
Когда что использовать (привет, кэп)
RTK Query лучше использовать, если:
- В приложении уже используется Redux и Redux Toolkit. Тогда RTK Query доступна из коробки и уже интегрирована с прочими инструментами.
- Требуется сложная логика управления данными. RTK Query - это более мощный инструмент и на нем можно построить более сложную систему.
React Query лучше использовать, если:
- В приложении не используется глобальное состояние или запросы данных с ним не взаимодействуют. React Query не предоставляет никаких способов создания глобального стейта или взаимодействия с ним, все только внутри компонента.
- Требуется простая логика обработки данных.
- Не хочется долго разбираться.
- Не хочется тянуть зависимости в виде Redux Toolkit.
#управлениесостоянием #redux
Frontend Mag
React Query vs RTK Query: A Comprehensive Comparison - Frontend Mag
We compare and contrast React Query vs RTK Query to help developers understand the similarities and differences between these two popular data management libraries for React applications.
👍1
Forwarded from Cat in Web
Переменная объявлена без указания типа:
const a = 42
Какой тип выведет для нее TypeScript?
const a = 42
Какой тип выведет для нее TypeScript?
Anonymous Quiz
72%
number
10%
42
13%
any
4%
unknown
1%
undefined
👍1👏1
Forwarded from TypeScript Challenge
Type Challenge #1042. IsNever
Ссылка на задачу
Ссылка на песочницу с тестами
Условие
Реализовать утилиту
Решение
Очень важная задачка, так как она затрагивает тему "распределенных" условных типов (описание в документации). Когда у нас есть дженерик с условным типом (extends) и он получает в качестве параметра union, он себя ведет "распределенно", то есть применяется к каждому элементу объединения.
И тут мы иногда имеем странные вещи:
Штука в том, что распределенный условный тип
Есть лайфхак, чтобы отменить распределенное поведение: обернуть обе части условного типа в квадратные скобки:
Зная это, решение составить очень просто:
#medium
Ссылка на задачу
Ссылка на песочницу с тестами
Условие
Реализовать утилиту
IsNever<T>
, которая возвращает true, если в качестве аргумента получает never. В остальных случаях возвращает false.Решение
Очень важная задачка, так как она затрагивает тему "распределенных" условных типов (описание в документации). Когда у нас есть дженерик с условным типом (extends) и он получает в качестве параметра union, он себя ведет "распределенно", то есть применяется к каждому элементу объединения.
И тут мы иногда имеем странные вещи:
// тут все нормально (без дженериков)
type A = never extends never ? 'yes' : 'no'
let a: A // a: 'yes'
// а тут что-то пошло не так
type B<T> = T extends never ? 'yes' : 'no'
let b: B<never> // b: never
Штука в том, что распределенный условный тип
B
ожидает union, а получает never, который интерпретирует как пустой union - и вообще не выполняется.Есть лайфхак, чтобы отменить распределенное поведение: обернуть обе части условного типа в квадратные скобки:
type B<T> = [T] extends [never] ? 'yes' : 'no'
let b: B<never> // b: 'yes'
Зная это, решение составить очень просто:
type IsNever<T> = [T] extends [never] ? true : false
#medium
GitHub
type-challenges/questions/01042-medium-isnever/README.md at main · type-challenges/type-challenges
Collection of TypeScript type challenges with online judge - type-challenges/type-challenges
👍2
Вариантность и совместимость типов в TypeScript
Попробуем разобраться, что такое ковариантность и контравариантность и на что они влияют в TypeScript.
Подтип и супертип
Сначала определим основные понятия - подтип и супертип.
Подтип расширяет супертип.
⁃ Dog - это подтип, Animal - это супертип.
⁃ String - это подтип, Object - это супертип.
⁃ number - это подтип, any - это супертип.
Мы пишем, что подтип меньше супертипа (SubType <: SuperType), однако по факту, в подтипе больше информации, чем в супертипе, потому что подтип буквально расширяет супертип. Подтип «избыточен» по сравнению с супертипом.
Любое значение с типом SubType всегда является одновременно значением типа SuperType, так как имеет все необходимые для SuperType поля.
Структурная типизация
Важно понимать, что в TypeScript подтип и супертип необязательно связаны отношениями наследования (class Dog extends Animal). TypeScript реализует структурную типизацию и ориентируется только на структуру типов.
Типы Animal и Dog никак не связаны между собой, но Dog все равно является подтипом для Animal.
Ковариантность как основная стратегия
Когда мы пытаемся присвоить значение какой-то переменной в TypeScript, компилятор смотрит, а соответствует ли тип этого значения объявленному типу этой переменной, совместимы ли они, можно ли положить это значение в эту переменную.
Есть конкретные правила для определения совместимости. В большинстве случаев верно утверждение:
⁃ Вы можете безопасно использовать подтип там, где ожидается супертип.
Довольно просто понять, почему так сделано - значение с типом SubType также является значением типа SuperType, поэтому тут нет противоречий. Если программа готова справиться с Animal (супертип), значит, она справится и с Dog (подтип), ведь он предоставляет те же самые данные.
Такая стратегия называется Ковариантностью. Она разрешает «избыточность» типа и запрещает «недостаточность».
Контравариантность для параметров функций
Стратегия ковариантности действует везде, кроме одного места - определения совместимости функций.
Казалось бы, Foo2 является подтипом для Foo1 (Foo2 <: Foo1), а значит функцию типа Foo2 должно быть можно положить в переменную с типом Foo1:
Но мы получаем ошибку. При этом наоборот все работает:
Здесь тоже несложно понять, почему так. Программа ожидает функцию с одним аргументом и передает ей только один аргумент. Если на этом месте окажется функция, ожидающая больше аргументов, она их просто не получит.
В обратную сторону тоже все хорошо. Даже если программа ожидает функцию с двумя аргументами и передает два аргумента, а в сигнатуре функции указан лишь один, ничего не сломается. Функция получит свой аргумент, а второй - ненужный - просто проигнорирует.
Такая стратегия называется «контравариантность». Она разрешает «недостаточность» типа и запрещает «избыточность».
#typescript
Попробуем разобраться, что такое ковариантность и контравариантность и на что они влияют в TypeScript.
Подтип и супертип
Сначала определим основные понятия - подтип и супертип.
Подтип расширяет супертип.
⁃ Dog - это подтип, Animal - это супертип.
⁃ String - это подтип, Object - это супертип.
⁃ number - это подтип, any - это супертип.
Мы пишем, что подтип меньше супертипа (SubType <: SuperType), однако по факту, в подтипе больше информации, чем в супертипе, потому что подтип буквально расширяет супертип. Подтип «избыточен» по сравнению с супертипом.
Любое значение с типом SubType всегда является одновременно значением типа SuperType, так как имеет все необходимые для SuperType поля.
Структурная типизация
Важно понимать, что в TypeScript подтип и супертип необязательно связаны отношениями наследования (class Dog extends Animal). TypeScript реализует структурную типизацию и ориентируется только на структуру типов.
type Animal = {
name: string
}
type Dog = {
name: string
breed: string
}
Типы Animal и Dog никак не связаны между собой, но Dog все равно является подтипом для Animal.
Ковариантность как основная стратегия
Когда мы пытаемся присвоить значение какой-то переменной в TypeScript, компилятор смотрит, а соответствует ли тип этого значения объявленному типу этой переменной, совместимы ли они, можно ли положить это значение в эту переменную.
Есть конкретные правила для определения совместимости. В большинстве случаев верно утверждение:
⁃ Вы можете безопасно использовать подтип там, где ожидается супертип.
let a: any = 42 // number <: any
let b: Animal = new Dog() // Dog <: Animal
Довольно просто понять, почему так сделано - значение с типом SubType также является значением типа SuperType, поэтому тут нет противоречий. Если программа готова справиться с Animal (супертип), значит, она справится и с Dog (подтип), ведь он предоставляет те же самые данные.
Такая стратегия называется Ковариантностью. Она разрешает «избыточность» типа и запрещает «недостаточность».
Контравариантность для параметров функций
Стратегия ковариантности действует везде, кроме одного места - определения совместимости функций.
type Foo1 = (param: number) => void
type Foo2 = (param1: number, param2: string) => void
Казалось бы, Foo2 является подтипом для Foo1 (Foo2 <: Foo1), а значит функцию типа Foo2 должно быть можно положить в переменную с типом Foo1:
let foo1: F1 = (param1: number, param2: string) => {} // Error
Но мы получаем ошибку. При этом наоборот все работает:
let foo2: F2 = (param1: number) => {} // Ok
Здесь тоже несложно понять, почему так. Программа ожидает функцию с одним аргументом и передает ей только один аргумент. Если на этом месте окажется функция, ожидающая больше аргументов, она их просто не получит.
В обратную сторону тоже все хорошо. Даже если программа ожидает функцию с двумя аргументами и передает два аргумента, а в сигнатуре функции указан лишь один, ничего не сломается. Функция получит свой аргумент, а второй - ненужный - просто проигнорирует.
Такая стратегия называется «контравариантность». Она разрешает «недостаточность» типа и запрещает «избыточность».
#typescript
👍3🌭1
Как устроена система типов в TypeScript
Статья (рус.): https://ru.hexlet.io/blog/posts/sistema-tipov-v-typescript
В статье разбирается стратегия ковариантности типов на основе принципе подстановки Барбары Лисков: поведение подтипа не должно противоречить поведению супертипа.
Объясняется, что подтип более строгий, чем супертип, так как ему соответствует меньше значений (собственно поэтому SubType < SuperType).
Немного говорится об иерархии типов в TypeScript, особенностях типов any, unknown, never и void.
Приводится простой способ проверить соответствие:
Статья также рассказывает о двух видах приведения типов:
⁃ восходящем (безопасное приведение подтипа к супертипу, происходит неявно)
⁃ нисходящее (приведение супертипа к подтипу, должно быть явным)
Обычно восходящее приведение происходит неявно, автоматически, но есть две ситуации, в которых оно запрещено - при работе с литеральными объектами (которые создаются на месте использования).
В общем, хорошая статья, подводящая под довольно сложную тему хороший теоретический базис.
#typescript
Статья (рус.): https://ru.hexlet.io/blog/posts/sistema-tipov-v-typescript
В статье разбирается стратегия ковариантности типов на основе принципе подстановки Барбары Лисков: поведение подтипа не должно противоречить поведению супертипа.
Объясняется, что подтип более строгий, чем супертип, так как ему соответствует меньше значений (собственно поэтому SubType < SuperType).
Немного говорится об иерархии типов в TypeScript, особенностях типов any, unknown, never и void.
Приводится простой способ проверить соответствие:
type A = string extends unknown ? true : false // true
type B = unknown extends string ? true : false // false
Статья также рассказывает о двух видах приведения типов:
⁃ восходящем (безопасное приведение подтипа к супертипу, происходит неявно)
⁃ нисходящее (приведение супертипа к подтипу, должно быть явным)
Обычно восходящее приведение происходит неявно, автоматически, но есть две ситуации, в которых оно запрещено - при работе с литеральными объектами (которые создаются на месте использования).
В общем, хорошая статья, подводящая под довольно сложную тему хороший теоретический базис.
#typescript
Хекслет
Как устроена система типов в TypeScript
Перевели большую статью бывшего разработчика Amazon Web Services Хэ Чжэнхао и узнали, что такое иерархия типов в TypeScript и как они соотносятся между собой.
👍3
TypeScript: Защитники типов (Type Guards)
Когда мы работаем с объединениями (union) в TypeScript, часто возникает необходимость совершить разные действия в зависимости от того, какой конкретно тип пришел.
Эта логика обычно реализуется с помощью разнообразных условий и проверок. Эти условия пишутся на JavaScript, но TypeScript умеет понимать некоторые из этих конструкций. То есть, если правильно написать проверку, то внутри блока if компилятор TypeScript будет понимать, что работает уже с уточненным типом.
Такие проверочные конструкции, которые понятны TS, называются «защитниками типа» - Type Guards. Они позволяют «сузить тип», выбрать нужный из объединения.
1. На основе типа данных
TypeScript понимает операторы
Эти операторы отлично распознаются компилятором внутри условий if-else, а также внутри тернарного оператора. Но не распознаются внутри конструкции switch.
2. С использование Tagged Union
Tagged Union (размеченное объединение) или Discriminated Union (дискриминантное объединение) - это объединение типов, у которых есть специальное общее поле со значением специфичным для конкретного типа. По этому полю эти типы можно различить.
Не имеет значения, как называется дискриминант, главное, чтобы он однозначно определял тип - TypeScript это понимает:
Такая проверка будет работать в любом условии: и в if-else, и в тернарном операторе, и даже в switch.
3. По наличию публичного поля
Почти то же самое, что и предыдущий пункт, но тут мы проверяем не значение поля, а его наличие - с помощью оператора
4. Функция-предикат
И наконец последняя (самая мощная) конструкция, которая может сузить объединение типов и которую может распознать TypeScript - это функция, которая возвращает предикат. Предикат - это однозначное утверждение, что значение, переданное этой функции, принадлежит к конкретному типу.
Такая функция оформляется особым образом - с помощью ключевого слова
Функция принимает параметр
Проверка внутри функции может быть абсолютно любая - TypeScript это не волнует. Нужно только, чтобы функция вернула логическое значение - true или false. Таким образом, ответственность за определение типа полностью перекладывается на разработчика.
Параметров тоже может быть сколько угодно, главное, чтобы один из них фигурировал в сигнатуре предиката.
#typescript
Когда мы работаем с объединениями (union) в TypeScript, часто возникает необходимость совершить разные действия в зависимости от того, какой конкретно тип пришел.
function move(obj: Dog | Fish | Bird) {
if (obj.type === ‘dog’) obj.run()
else if (obj.type === ‘fish’) obj.swim()
else obj.fly()
}
Эта логика обычно реализуется с помощью разнообразных условий и проверок. Эти условия пишутся на JavaScript, но TypeScript умеет понимать некоторые из этих конструкций. То есть, если правильно написать проверку, то внутри блока if компилятор TypeScript будет понимать, что работает уже с уточненным типом.
function move(obj: Dog | Fish | Bird) {
if (obj.type === ‘dog’) {
// Здесь TS уже уверен, что работает именно с Dog
obj.run()
}
// …
}
Такие проверочные конструкции, которые понятны TS, называются «защитниками типа» - Type Guards. Они позволяют «сузить тип», выбрать нужный из объединения.
1. На основе типа данных
TypeScript понимает операторы
instanceof
и typeof
.
function foo(param: number | string) {
if (typeof param === ‘string’) {
param.toUpperCase()
} else {
param.toFixed(2)
}
function move(obj: Dog | Fish | Bird) {
if (obj instanceof Dog) obj.run()
else if (obj instanceof Fish) obj.swim()
else obj.fly()
}
Эти операторы отлично распознаются компилятором внутри условий if-else, а также внутри тернарного оператора. Но не распознаются внутри конструкции switch.
2. С использование Tagged Union
Tagged Union (размеченное объединение) или Discriminated Union (дискриминантное объединение) - это объединение типов, у которых есть специальное общее поле со значением специфичным для конкретного типа. По этому полю эти типы можно различить.
class Bird {
type: ‘bird’ = ‘bird’
}
class Fish {
type: ‘fish’ = ‘fish’
}
Не имеет значения, как называется дискриминант, главное, чтобы он однозначно определял тип - TypeScript это понимает:
function move(obj: Dog | Fish | Bird) {
if (obj.type === ‘dog’) obj.run()
else if (obj.type === ‘fish’) obj.swim()
else obj.fly()
}
Такая проверка будет работать в любом условии: и в if-else, и в тернарном операторе, и даже в switch.
3. По наличию публичного поля
Почти то же самое, что и предыдущий пункт, но тут мы проверяем не значение поля, а его наличие - с помощью оператора
in
.
class Author {
public book: string = ‘Odyssey’
}
class Artist {
public painting: string = ‘Mona Lisa’
}
function getMasterpiece(creator: Author | Artist) {
let masterpiece;
if (‘book’ in creator) masterpiece = creator.book;
else masterpiece = creator.painting;
}
4. Функция-предикат
И наконец последняя (самая мощная) конструкция, которая может сузить объединение типов и которую может распознать TypeScript - это функция, которая возвращает предикат. Предикат - это однозначное утверждение, что значение, переданное этой функции, принадлежит к конкретному типу.
Такая функция оформляется особым образом - с помощью ключевого слова
is
:
function isDog(obj: Dog | Fish | Bird): obj is Dog {
return obj.type === ‘dog’
}
Функция принимает параметр
obj
и после проведения проверки утверждает, принадлежит ли obj
к типу Dog
.Проверка внутри функции может быть абсолютно любая - TypeScript это не волнует. Нужно только, чтобы функция вернула логическое значение - true или false. Таким образом, ответственность за определение типа полностью перекладывается на разработчика.
Параметров тоже может быть сколько угодно, главное, чтобы один из них фигурировал в сигнатуре предиката.
function move(obj: Dog | Fish) {
if (isDog(obj)) {
obj.run()
} else {
obj.swim()
}
}
#typescript
👍3🔥2
React useTransition: решает все проблемы с производительностью или нет?
Статья (англ.): https://www.developerway.com/posts/use-transition
В React 18 появился "конкурентный рендеринг", цель которого решить проблемы с производительностью веб-приложений.
‼️Суть проблемы
Обновление стейта в React - штука синхронная. Как только стейт начал обновляться, поток выполнения кода блокируется. Пока не будут произведены все необходимые вычисления, пока не перерендерятся все изменившиеся компоненты, поток будет занят. Если в это время пользователь захочет повзаимодействовать со страницей, у него ничего не получится.
Например, мы собираемся вывести на страницу большой список из сотни пунктов. Каждый пункт - это немаленький компонент, который требует времени на отрисовку. Мы закидываем этот список в стейт - и обновление понеслось. Пока не отрендерятся все 100 пунктов, страница будет "висеть", пользователь не сможет никуда кликнуть, ничего прокрутить.
🔆 Переходные изменения для решения проблемы
React 18 вводит понятие "переходных изменений" - transitions. Переходное изменение в данном случае - это НЕ критичное изменение стейта. Например, вот этот самый список: отрисовать его не так критично, как сохранить интерактивность. Он и так большой, ничего страшного, если придется подождать чуть больше до его появления на странице.
Мы создаем transition и уже внутри него вносим изменения в стейт (закидываем список). React понимает, что это изменения и все изменения, вызванные им, не суперважные. Он начинает их обрабатывать, но если в это время пользователь начнет двигать мышкой, то React все бросит и переключится на пользователя.
Таким образом, "переходы" обрабатываются как бы "в фоне", хотя это не очень правильный термин, ведь JavaScript по-прежнему остается однопоточным языком. Правильнее будет сказать, что обработка переходного изменения просто прерывается, если появляются более срочные дела, а потом React снова к ней возвращается.
Выглядит это все вот так:
‼️Проблема переходных изменений
Проблема скрывается вот тут:
При запуске нового перехода флаг
В статье эта проблема очень хорошо разобрана на примере.
Берем простую страницу с тремя табами, у одного из которых очень тяжелый контент. Смену таба делаем переходным изменением, чтобы пока контент рендерится, пользователь не страдал. Это прекрасно решает проблему открытия тяжелого таба, но создает новую - при открытии легкого таба после тяжелого.
- мы хотим открыть тяжелый таб, запускаем переход
- пользователь ждет (показываем его лоадер), но в это время он может взаимодействовать со страницей (например, нажать другой таб)
- когда переход обработан, отображается тяжелый таб
- теперь пользователь кликает на легкий таб, казалось бы он должен открыться сразу же, но нет, интерфейс виснет
Виснет он именно потому, что меняется флаг
Решение у проблемы есть - это мемоизация. Но в целом следует хорошенько думать, прежде чем использовать конкурентный рендеринг. Это очень тонкий механизм, который требует ясного понимания подкапотной работы React.
#ссылки #concurrent #важно
Статья (англ.): https://www.developerway.com/posts/use-transition
В React 18 появился "конкурентный рендеринг", цель которого решить проблемы с производительностью веб-приложений.
‼️Суть проблемы
Обновление стейта в React - штука синхронная. Как только стейт начал обновляться, поток выполнения кода блокируется. Пока не будут произведены все необходимые вычисления, пока не перерендерятся все изменившиеся компоненты, поток будет занят. Если в это время пользователь захочет повзаимодействовать со страницей, у него ничего не получится.
Например, мы собираемся вывести на страницу большой список из сотни пунктов. Каждый пункт - это немаленький компонент, который требует времени на отрисовку. Мы закидываем этот список в стейт - и обновление понеслось. Пока не отрендерятся все 100 пунктов, страница будет "висеть", пользователь не сможет никуда кликнуть, ничего прокрутить.
🔆 Переходные изменения для решения проблемы
React 18 вводит понятие "переходных изменений" - transitions. Переходное изменение в данном случае - это НЕ критичное изменение стейта. Например, вот этот самый список: отрисовать его не так критично, как сохранить интерактивность. Он и так большой, ничего страшного, если придется подождать чуть больше до его появления на странице.
Мы создаем transition и уже внутри него вносим изменения в стейт (закидываем список). React понимает, что это изменения и все изменения, вызванные им, не суперважные. Он начинает их обрабатывать, но если в это время пользователь начнет двигать мышкой, то React все бросит и переключится на пользователя.
Таким образом, "переходы" обрабатываются как бы "в фоне", хотя это не очень правильный термин, ведь JavaScript по-прежнему остается однопоточным языком. Правильнее будет сказать, что обработка переходного изменения просто прерывается, если появляются более срочные дела, а потом React снова к ней возвращается.
Выглядит это все вот так:
const [list, setList] = useState([])
const [isPending, startTransition] = useTransition()
const showList = () => {
startTransition(() => {
setList(hugeList)
})
}
‼️Проблема переходных изменений
Проблема скрывается вот тут:
const [isPending, startTransition] = useTransition()
При запуске нового перехода флаг
isPending
изменяется, что вызывает дополнительный перерендер компонента. Если компонент сам по себе тяжелый, то этот перерендер сам заблокирует интерактивность страницы.В статье эта проблема очень хорошо разобрана на примере.
Берем простую страницу с тремя табами, у одного из которых очень тяжелый контент. Смену таба делаем переходным изменением, чтобы пока контент рендерится, пользователь не страдал. Это прекрасно решает проблему открытия тяжелого таба, но создает новую - при открытии легкого таба после тяжелого.
- мы хотим открыть тяжелый таб, запускаем переход
- пользователь ждет (показываем его лоадер), но в это время он может взаимодействовать со страницей (например, нажать другой таб)
- когда переход обработан, отображается тяжелый таб
- теперь пользователь кликает на легкий таб, казалось бы он должен открыться сразу же, но нет, интерфейс виснет
Виснет он именно потому, что меняется флаг
isPending
и текущий компонент перерендеривается, а текущий у нас сейчас - тяжелый таб. То есть прежде чем перейти на легкий таб, мы перерендериваем тяжелый.Решение у проблемы есть - это мемоизация. Но в целом следует хорошенько думать, прежде чем использовать конкурентный рендеринг. Это очень тонкий механизм, который требует ясного понимания подкапотной работы React.
#ссылки #concurrent #важно
Developerway
React useTransition: performance game changer or...?
Looking into what React Concurrent Rendering is, what hooks like useTransition and useDeferredValue do, what are the benefits and downsides of using them.
👍6🔥4
Принципы SOLID в React: так ли все с ними гладко? (Часть 1)
Статья (рус.):https://nuancesprog.ru/p/19540/
Автор разбирает тонкости и подводные камни SOLID применительно к React-приложениям.
Основная идея в том, что принципы SOLID в нашем сознании тесно связаны с ООП. Однако в React они также применимы, более того, иногда они применяются без нашего ведома. Поэтому нам стоит разобраться в этом и писать код более осмысленно.
Принцип единой ответственности
Должна быть только одна причина для существования компонента. Но у нас же есть "контейнерные" компоненты, которые могут управлять сразу несколькими дочерними подкомпонентами.
Автор предлагает рассматривать принцип единой ответственности с такой точки зрения:
- компоненты-менеджеры (контейнеры) отвечают только за композицию дочерних компонентов, сами при этом не имеют собственного JSX.
- а компоненты-работники (презентационные) ничего не знают про бизнес логику и только отрисовывают свою часть страницы.
Принцип открытости/закрытости
Компонент должен быть открыт для расширений и закрыт для модификаций. Тут важно понимать, что модификации имеют в виду только с точки зрения потребителя компонента, так как для самого компонента и то, и то - одно и то же. Это основа принципа обратной совместимости.
В React у нас есть несколько способов расширять компонент, не модифицируя его. Посмотрим на примере компонента кнопки, у которого есть проп icon - иконка. Иконка размещается перед текстом кнопки. Поступила задача - теперь должна быть возможность располагать иконку после текста. Есть несколько способов решения:
- добавить проп iconPlacement
- добавить новые свойства iconStart и iconEnd (продолжая поддерживать icon)
- или вообще создать новый компонент IconButton, наследующий от Button
Что выбрать, во многом зависит от ситуации.
Принцип подстановки Лисков
Довольно сложный для понимания принцип: вы должны быть в состоянии заменить любой супертип на его подтип, например, все Button на IconButton. Однако чаще всего это невозможно, так как IconButton требует дополнительных пропсов.
Однако мы и не должны это делать. Важно понимать, для чего мы создали IconButton. Он не должен заменять Button, в целом это совершенно независимый компонент, который никак не полагается на наследование от Button. Наоборот, он использует композицию.
Однако принцип Лисков вполне работает в React, в тех ситуациях, когда такая замена изначально предусматривается. Например, компонент Button фактически наследует от HTMLButtonElement - он должен и может везде его заменить.
#ссылки #паттерны
Статья (рус.):https://nuancesprog.ru/p/19540/
Автор разбирает тонкости и подводные камни SOLID применительно к React-приложениям.
Основная идея в том, что принципы SOLID в нашем сознании тесно связаны с ООП. Однако в React они также применимы, более того, иногда они применяются без нашего ведома. Поэтому нам стоит разобраться в этом и писать код более осмысленно.
Принцип единой ответственности
Должна быть только одна причина для существования компонента. Но у нас же есть "контейнерные" компоненты, которые могут управлять сразу несколькими дочерними подкомпонентами.
Автор предлагает рассматривать принцип единой ответственности с такой точки зрения:
- компоненты-менеджеры (контейнеры) отвечают только за композицию дочерних компонентов, сами при этом не имеют собственного JSX.
- а компоненты-работники (презентационные) ничего не знают про бизнес логику и только отрисовывают свою часть страницы.
Принцип открытости/закрытости
Компонент должен быть открыт для расширений и закрыт для модификаций. Тут важно понимать, что модификации имеют в виду только с точки зрения потребителя компонента, так как для самого компонента и то, и то - одно и то же. Это основа принципа обратной совместимости.
В React у нас есть несколько способов расширять компонент, не модифицируя его. Посмотрим на примере компонента кнопки, у которого есть проп icon - иконка. Иконка размещается перед текстом кнопки. Поступила задача - теперь должна быть возможность располагать иконку после текста. Есть несколько способов решения:
- добавить проп iconPlacement
- добавить новые свойства iconStart и iconEnd (продолжая поддерживать icon)
- или вообще создать новый компонент IconButton, наследующий от Button
Что выбрать, во многом зависит от ситуации.
Принцип подстановки Лисков
Довольно сложный для понимания принцип: вы должны быть в состоянии заменить любой супертип на его подтип, например, все Button на IconButton. Однако чаще всего это невозможно, так как IconButton требует дополнительных пропсов.
Однако мы и не должны это делать. Важно понимать, для чего мы создали IconButton. Он не должен заменять Button, в целом это совершенно независимый компонент, который никак не полагается на наследование от Button. Наоборот, он использует композицию.
Однако принцип Лисков вполне работает в React, в тех ситуациях, когда такая замена изначально предусматривается. Например, компонент Button фактически наследует от HTMLButtonElement - он должен и может везде его заменить.
#ссылки #паттерны
NOP::Nuances of programming
Принципы SOLID в React: так ли все с ними гладко?
Принципы SOLID принято описывать только в положительном свете. Поэтому многие нюансы часто упускаются из виду. Сегодня поговорим о том, применимы ли вообще принципы SOLID в React-приложении и какими ключевыми особенностями обладает каждый из них.
👍4❤2
Принципы SOLID в React: так ли все с ними гладко? (Часть 2)
Статья (рус.):https://nuancesprog.ru/p/19540/
Принцип разделения интерфейсов
Компонент не должен зависеть от свойств, которые он не использует. Прекрасный принцип, с которым сложно поспорить.
В качестве примера приводится компонент UserAvatar, который в качестве пропа получает объект типа User. И хотя аватар исползует только пару свойств юзера, он все равно вынужден знать об этом типе. Правильнее будет передать в компонент только те пропсы, которые ему действительно нужны - изменить его интерфейс.
Принцип инверсии зависимостей
Реализация должна зависеть от абстракций и не должна зависеть от других реализаций.
React в принципе построен на этом принципе, мы используем его, даже не замечая.
У нас есть два вида зависимостей:
- прямая зависимость - это импорты. Мы прямо импортируем что-то из соответствующих модулей, и если в этих модулях что-то изменится, нам будет больно.
- обратная зависимость - это то, что мы получаем в виде параметров. От них мы тоже зависим, но хотя бы можем их контролировать - определить абстракцию - интерфейс.
В итоге наш компонент UserAvatar в исходном виде нарушает сразу три приципа SOLID:
- единой ответственности (компонент в курсе бизнес-логики, знает, как устроен user)
- разделения интерфейсов
- инверсии зависимостей (импортирует вместо того, чтобы принимать, зависит от реализации, а не от абстракции)
Автор отдельно отмечает, что принцип инверсии зависимостей это не только про обработчики событий, которые мы передаем дочерним компонентам. На самом деле композиция в React - это и есть инверсия зависимостей.
Проп children - это тоже инверсия зависимостей. Благодаря ей компонент-работник, который ничего не должен знать о бизнес-логике, может содержать в себе компоненты-менеджеры, не нарушая ни одного принципа, так как ничего об этом не знает. Более того, компонент даже может определить интерфейс для этого пропа (например, принимать только строку) - то есть зависит от абстракции.
В React инверсия зависимостей - это перенос ответственности на родителя. Важно соблюдать разумные границы в реализации этого принципа, ведь ответственности может стать очень много. Иногда можно использовать и прямые зависимости.
#ссылки #паттерны
Статья (рус.):https://nuancesprog.ru/p/19540/
Принцип разделения интерфейсов
Компонент не должен зависеть от свойств, которые он не использует. Прекрасный принцип, с которым сложно поспорить.
В качестве примера приводится компонент UserAvatar, который в качестве пропа получает объект типа User. И хотя аватар исползует только пару свойств юзера, он все равно вынужден знать об этом типе. Правильнее будет передать в компонент только те пропсы, которые ему действительно нужны - изменить его интерфейс.
Принцип инверсии зависимостей
Реализация должна зависеть от абстракций и не должна зависеть от других реализаций.
React в принципе построен на этом принципе, мы используем его, даже не замечая.
У нас есть два вида зависимостей:
- прямая зависимость - это импорты. Мы прямо импортируем что-то из соответствующих модулей, и если в этих модулях что-то изменится, нам будет больно.
- обратная зависимость - это то, что мы получаем в виде параметров. От них мы тоже зависим, но хотя бы можем их контролировать - определить абстракцию - интерфейс.
В итоге наш компонент UserAvatar в исходном виде нарушает сразу три приципа SOLID:
- единой ответственности (компонент в курсе бизнес-логики, знает, как устроен user)
- разделения интерфейсов
- инверсии зависимостей (импортирует вместо того, чтобы принимать, зависит от реализации, а не от абстракции)
Автор отдельно отмечает, что принцип инверсии зависимостей это не только про обработчики событий, которые мы передаем дочерним компонентам. На самом деле композиция в React - это и есть инверсия зависимостей.
Проп children - это тоже инверсия зависимостей. Благодаря ей компонент-работник, который ничего не должен знать о бизнес-логике, может содержать в себе компоненты-менеджеры, не нарушая ни одного принципа, так как ничего об этом не знает. Более того, компонент даже может определить интерфейс для этого пропа (например, принимать только строку) - то есть зависит от абстракции.
В React инверсия зависимостей - это перенос ответственности на родителя. Важно соблюдать разумные границы в реализации этого принципа, ведь ответственности может стать очень много. Иногда можно использовать и прямые зависимости.
#ссылки #паттерны
NOP::Nuances of programming
Принципы SOLID в React: так ли все с ними гладко?
Принципы SOLID принято описывать только в положительном свете. Поэтому многие нюансы часто упускаются из виду. Сегодня поговорим о том, применимы ли вообще принципы SOLID в React-приложении и какими ключевыми особенностями обладает каждый из них.
👍6
Зачем писать юнит-тесты на фронтенд?
Статья (рус.): https://habr.com/ru/companies/nordclan/articles/755302/
Статья для тех, кто (я 🙃) очень плохо понимает, как вообще тестировать фронтенд. Методички не будет, зато будет практический пример, очень понятный.
Самый важный посыл - нужно отделять логику от UI - и помещать ее, например, в хуки. Тогда мы сможем отдельно протестировать хук. Кроме того, мы сможем подкинуть компоненту поддельную логику и проверить, как он с ней взаимодействует.
В качестве примера у нас тут игра судоку. Автор сначала пишет код без тестов и засовывает всю логику прямо в компонент, пишет тесты на то, что в ячейку можно ввести значение, но быстро сталкивается с тем, что никак нельзя проверить состояние игры - сохранилось ли введенное значение.
Пункт 1 - сначала пишем тесты, потом реализацию.
Тогда автор выносит всю логику работы с игровым полем в хук и тестирует отдельно хук. Когда речь идет о логике, кажется, вполне понятно, что нужно тестировать. Проверяем начальное состояние поля. Пытаемся изменить значение в конкретной ячейке и проверяем измененное состояние.
Пункт 2 - отделяем логику от UI.
А дальше финт ушами - мы передаем наш хук в компонент в качестве пропа! То есть всю логику целиком передаем в компонент, чтобы он ее использовал, не задумываясь о том, что там под капотом.
(Тут кстати не забываем про принцип инверсии зависимостей. Компонент строго определяет интерфейс пропа, который он хочет видеть, и полностью полагается на него, а не на конкретную реализацию.)
Теперь при тестировании компонента мы, например, можем подкинуть ему фальшивый хук, в котором замоканы все нужные методы.
Что же тестировать в самом компоненте?
Например то, что он вызывает нужный метод в нужный момент. Мы имитируем ввод данных в ячейку и проверяем, был ли вызван метод, изменяющий состояние.
Пункт 3 - когда логика протестирована, мы тестируем взаимодействие UI с логикой.
#ссылки #тестирование
Статья (рус.): https://habr.com/ru/companies/nordclan/articles/755302/
Статья для тех, кто (я 🙃) очень плохо понимает, как вообще тестировать фронтенд. Методички не будет, зато будет практический пример, очень понятный.
Самый важный посыл - нужно отделять логику от UI - и помещать ее, например, в хуки. Тогда мы сможем отдельно протестировать хук. Кроме того, мы сможем подкинуть компоненту поддельную логику и проверить, как он с ней взаимодействует.
В качестве примера у нас тут игра судоку. Автор сначала пишет код без тестов и засовывает всю логику прямо в компонент, пишет тесты на то, что в ячейку можно ввести значение, но быстро сталкивается с тем, что никак нельзя проверить состояние игры - сохранилось ли введенное значение.
Пункт 1 - сначала пишем тесты, потом реализацию.
Тогда автор выносит всю логику работы с игровым полем в хук и тестирует отдельно хук. Когда речь идет о логике, кажется, вполне понятно, что нужно тестировать. Проверяем начальное состояние поля. Пытаемся изменить значение в конкретной ячейке и проверяем измененное состояние.
Пункт 2 - отделяем логику от UI.
А дальше финт ушами - мы передаем наш хук в компонент в качестве пропа! То есть всю логику целиком передаем в компонент, чтобы он ее использовал, не задумываясь о том, что там под капотом.
(Тут кстати не забываем про принцип инверсии зависимостей. Компонент строго определяет интерфейс пропа, который он хочет видеть, и полностью полагается на него, а не на конкретную реализацию.)
Теперь при тестировании компонента мы, например, можем подкинуть ему фальшивый хук, в котором замоканы все нужные методы.
Что же тестировать в самом компоненте?
Например то, что он вызывает нужный метод в нужный момент. Мы имитируем ввод данных в ячейку и проверяем, был ли вызван метод, изменяющий состояние.
Пункт 3 - когда логика протестирована, мы тестируем взаимодействие UI с логикой.
#ссылки #тестирование
Хабр
Зачем писать юнит-тесты на фронтенд?
Привет, хабр! Меня зовут Александр, я работаю фронтенд-разработчиком в компании Nord Clan. Сегодня речь пойдет про тесты… Про юнит-тесты. Думаю, что почти все слышали про юнит-тесты, пробовали их...
👍2🔥1
Testing Library. Вступление
Незапланированно и стремительно перехожу к рассмотрению Testing Library и ее субпакетов.
Вот тут сайтик с документацией и примерами: https://testing-library.com/
Что это вообще за штука такая?
По сути это набор утилит для тестирования DOM, то есть нашего с вами фронтенда. Они работают с любым DOM-подобным ресурсом и помогают находить элементы и эмулировать события, как это мог бы делать пользователь.
Философия Testing Library:
«Чем больше тесты похожи на реальное использование вашего продукта, тем больше уверенности они дают».
Эта фраза преследует вас по всей доке. Она означает, что мы максимально отходим от реализации и работаем только с тем, что есть на странице - что видит пользователь. Тесты ничего не знают о вашем коде, а значит, они не сломаются, если код изменится.
Testing Library может работать с реальным DOM из браузера, а также с его эмуляциями (JSDOM, Jest).
У них есть пакеты с базовой функциональностью, а также отдельные модули для популярных фреймворков, которые добавляют специфические утилиты.
Итак, Testing Library это большой набор хелперов для работы с DOM в стиле реального пользователя. Ее удобно использовать в тестах, но это не тест-раннер и не полноценный фреймворк для тестирования - просто хелпер, не привязанный к другим технологиям (только к DOM).
Утверждается, что мы можем использовать Testing Library для любых видов тестов, от юнитов до e2e (есть даже интеграция с cypress).
Итак, основной принцип: мы работаем с DOM, а не с инстансами компонентов.
#тестирование #testinglibrary #документация
Незапланированно и стремительно перехожу к рассмотрению Testing Library и ее субпакетов.
Вот тут сайтик с документацией и примерами: https://testing-library.com/
Что это вообще за штука такая?
По сути это набор утилит для тестирования DOM, то есть нашего с вами фронтенда. Они работают с любым DOM-подобным ресурсом и помогают находить элементы и эмулировать события, как это мог бы делать пользователь.
Философия Testing Library:
«Чем больше тесты похожи на реальное использование вашего продукта, тем больше уверенности они дают».
Эта фраза преследует вас по всей доке. Она означает, что мы максимально отходим от реализации и работаем только с тем, что есть на странице - что видит пользователь. Тесты ничего не знают о вашем коде, а значит, они не сломаются, если код изменится.
Testing Library может работать с реальным DOM из браузера, а также с его эмуляциями (JSDOM, Jest).
У них есть пакеты с базовой функциональностью, а также отдельные модули для популярных фреймворков, которые добавляют специфические утилиты.
Итак, Testing Library это большой набор хелперов для работы с DOM в стиле реального пользователя. Ее удобно использовать в тестах, но это не тест-раннер и не полноценный фреймворк для тестирования - просто хелпер, не привязанный к другим технологиям (только к DOM).
Утверждается, что мы можем использовать Testing Library для любых видов тестов, от юнитов до e2e (есть даже интеграция с cypress).
Итак, основной принцип: мы работаем с DOM, а не с инстансами компонентов.
#тестирование #testinglibrary #документация
Testing-Library
Testing Library | Testing Library
Simple and complete testing utilities that encourage good testing practices
👍3
Testing Library. Запросы
Основная задача при работе с DOM - поиск нужных элементов. За это у нас отвечает пакет @testing-library/dom.
Что искать?
Testing Library придерживается философии, что на элементы нужно смотреть с точки зрения пользователя, а не программиста.
⁃ Пользователь ищет элемент не по классу или testid, а прежде всего по его роли: например, поле ввода или кнопка.
⁃ Элементы формы можно искать по тексту связанного с ними элементу label или по плейсхолдеру. В крайнем случае по значению, которое находится в поле в данный момент.
⁃ Неинтерактивные элементы (параграфы, дивы) можно искать по тексту.
⁃ Есть еще варианты искать по тексту атрибутов alt и title, но он уже менее желательный.
⁃ И наконец в конце списка идет поиск по testid.
Как искать?
Библиотека предоставляет целую кучу методов для поиска, разделенных на три группы - отличаются мелкими деталями.
⁃ getBy… и getAllBy… (например, `getByRole`)
⁃ queryBy… и queryAllBy…
⁃ findBy… и findAllBy…
Очевидно, что методы с префиксами
Первое отличие состоит в том, как этим методы реагируют на неудачу - если не нашлось ничего. Методы группы
Вторая особенность - методы
Пробуем
Все эти методы можно импортировать из библиотеки напрямую. В качестве первого аргумента они ожидают HTML-контейнер, внутри которого нужно искать.
Рабочий пример: https://codesandbox.io/p/devbox/compassionate-mestorf-68kddy
#тестирование #testinglibrary #документация #примерыкода
Основная задача при работе с DOM - поиск нужных элементов. За это у нас отвечает пакет @testing-library/dom.
Что искать?
Testing Library придерживается философии, что на элементы нужно смотреть с точки зрения пользователя, а не программиста.
⁃ Пользователь ищет элемент не по классу или testid, а прежде всего по его роли: например, поле ввода или кнопка.
⁃ Элементы формы можно искать по тексту связанного с ними элементу label или по плейсхолдеру. В крайнем случае по значению, которое находится в поле в данный момент.
⁃ Неинтерактивные элементы (параграфы, дивы) можно искать по тексту.
⁃ Есть еще варианты искать по тексту атрибутов alt и title, но он уже менее желательный.
⁃ И наконец в конце списка идет поиск по testid.
Как искать?
Библиотека предоставляет целую кучу методов для поиска, разделенных на три группы - отличаются мелкими деталями.
⁃ getBy… и getAllBy… (например, `getByRole`)
⁃ queryBy… и queryAllBy…
⁃ findBy… и findAllBy…
Очевидно, что методы с префиксами
getBy
, queryBy
и findBy
ищут и возвращают один элемент. При этом если найдется несколько, то все они выбросят ошибку. Методы getAllBy
, queryAllBy
и findAllBy
ищут все подходящие элементы и возвращают массив.Первое отличие состоит в том, как этим методы реагируют на неудачу - если не нашлось ничего. Методы группы
query
не очень драматизируют по этому поводу и возвращают либо null
, либо пустой массив. Остальные сразу кидают ошибку.Вторая особенность - методы
find
возвращают промисы. Благодаря этому они могут «ждать», пока нужный элемент появится на странице. Остальные по умолчанию так не могут, если элемента нет, так нет.Пробуем
Все эти методы можно импортировать из библиотеки напрямую. В качестве первого аргумента они ожидают HTML-контейнер, внутри которого нужно искать.
Рабочий пример: https://codesandbox.io/p/devbox/compassionate-mestorf-68kddy
#тестирование #testinglibrary #документация #примерыкода
👍4
Testing Library. Screen
Итак, в Testing Library есть куча методов для поиска элементов в DOM, но они все требуют передавать им первым аргументом HTML-контейнер.
Есть другой подход, более удобный. Библиотека экспортирует объект screen, в котором тоже есть все эти методы, но уже с предустановленным контейнером. Как нетрудно догадаться, в его роли выступает document.body.
Однако как будто нет способа установить свой собственный body для screen, он берется из глобального скоупа. То есть у нас должен быть глобальный document, но так как мы не работаем с браузером, его нет.
Можно попробовать установить его искусственно:
Но у меня что-то не получилось, все равно кидает ошибку, что document.body недоступно.
Поэтому мы возьмем вместо jsdom другой пакет, который сделает это за нас (возьмет jsdom и закинет его на глобальный уровень, как будто мы в браузере). Дальше можно будет работать как будто внутри браузера.
Пакет называется global-jsdom, а чтобы все завелось нужно подключить модуль ‘global-jsdom/register’.
Этих эманаций оказалось достаточно, чтобы screen соизволил заработать, так что теперь можем брать методы у него.
Работающий пример тут: https://codesandbox.io/p/devbox/testing-library-global-jsdom-screen-react-junior-forked-8hrw8y
#тестирование #testinglibrary #документация #примерыкода
Итак, в Testing Library есть куча методов для поиска элементов в DOM, но они все требуют передавать им первым аргументом HTML-контейнер.
Есть другой подход, более удобный. Библиотека экспортирует объект screen, в котором тоже есть все эти методы, но уже с предустановленным контейнером. Как нетрудно догадаться, в его роли выступает document.body.
Однако как будто нет способа установить свой собственный body для screen, он берется из глобального скоупа. То есть у нас должен быть глобальный document, но так как мы не работаем с браузером, его нет.
Можно попробовать установить его искусственно:
const dom = new JSDOM(‘’)
global.window = dom.window;
global.document = dom.window.document;
Но у меня что-то не получилось, все равно кидает ошибку, что document.body недоступно.
Поэтому мы возьмем вместо jsdom другой пакет, который сделает это за нас (возьмет jsdom и закинет его на глобальный уровень, как будто мы в браузере). Дальше можно будет работать как будто внутри браузера.
Пакет называется global-jsdom, а чтобы все завелось нужно подключить модуль ‘global-jsdom/register’.
require('global-jsdom/register')
// теперь можно взаимодействовать с DOM глобально
document.body.innerHTML = ''
Этих эманаций оказалось достаточно, чтобы screen соизволил заработать, так что теперь можем брать методы у него.
Работающий пример тут: https://codesandbox.io/p/devbox/testing-library-global-jsdom-screen-react-junior-forked-8hrw8y
#тестирование #testinglibrary #документация #примерыкода
👍3
Testing Library. Расширение и песочница
Есть прикольное расширение, которое помогает выбрать подходящий метод для поиска элемента на странице: https://chromewebstore.google.com/detail/testing-playground/hejbmebodbijjdhflfknehhcgaklhano
Устанавливаем его, в DevTools появляется новая вкладка Testing Playground.
Открываем ее, наводим курсор на нужный элемент, и расширение пытается подобрать самый подходящий метод с учетом всех best practices.
А еще есть песочница, где можно ввести любой кусок HTML и поиграться с запросами - https://testing-playground.com/
#тестирование #testinglibrary #документация
Есть прикольное расширение, которое помогает выбрать подходящий метод для поиска элемента на странице: https://chromewebstore.google.com/detail/testing-playground/hejbmebodbijjdhflfknehhcgaklhano
Устанавливаем его, в DevTools появляется новая вкладка Testing Playground.
Открываем ее, наводим курсор на нужный элемент, и расширение пытается подобрать самый подходящий метод с учетом всех best practices.
А еще есть песочница, где можно ввести любой кусок HTML и поиграться с запросами - https://testing-playground.com/
#тестирование #testinglibrary #документация
Google
Testing Playground - Chrome Web Store
Simple and complete DOM testing playground that encourage good testing practices.
👍3
Forwarded from Cat in Web
React Reconciliation: как работает и зачем нам это знать
Статья (англ.): https://www.developerway.com/posts/reconciliation-in-react
Перевод: https://gist.github.com/zagazat/db926ec7ab69061934246a55b64913c3
Реконсиляция в React - это процесс сравнения старого и нового состояния приложения, определение, что изменилось и что нужно перерендерить.
React берет старый и обновленный виртуальный DOM и сравнивает их.
Идея в том, чтобы найти оптимальный способ перерендеринга элементов в реальном DOM, чтобы улучшить производительность. React ищет все возможности, чтобы не создавать новые элементы, а переиспользовать уже существующие, просто внеся в них минимальные изменения.
В статье Надя Макаревич рассматривает некоторые неочевидные подводные камни такой оптимизации и способы их решения. Например, в некоторых ситуациях React переиспользует старый элемент (включая его стейт), даже если мы этого не хотим.
В качестве решения проблемы рассматриваются два подхода: с позицией элемента в массиве и с атрибутом key.
#react
Статья (англ.): https://www.developerway.com/posts/reconciliation-in-react
Перевод: https://gist.github.com/zagazat/db926ec7ab69061934246a55b64913c3
Реконсиляция в React - это процесс сравнения старого и нового состояния приложения, определение, что изменилось и что нужно перерендерить.
React берет старый и обновленный виртуальный DOM и сравнивает их.
Идея в том, чтобы найти оптимальный способ перерендеринга элементов в реальном DOM, чтобы улучшить производительность. React ищет все возможности, чтобы не создавать новые элементы, а переиспользовать уже существующие, просто внеся в них минимальные изменения.
В статье Надя Макаревич рассматривает некоторые неочевидные подводные камни такой оптимизации и способы их решения. Например, в некоторых ситуациях React переиспользует старый элемент (включая его стейт), даже если мы этого не хотим.
В качестве решения проблемы рассматриваются два подхода: с позицией элемента в массиве и с атрибутом key.
#react
Developerway
React reconciliation: how it works and why should we care
Looking in detail into how React reconciliation algorithm works. Explore in the process quirks of conditional rendering, the 'key' attribute, and why we shouldn't declare components inside other components.
👍3
byRole
Итак, начнем с подробного разбора самого главного запроса в Testing Library:
Документация: https://testing-library.com/docs/queries/byrole
Собственно роль
Первым параметром метод принимает собственно роль нужного элемента в виде строки. Это может быть
Речь идет не об атрибуте
Опции
Вторым параметром можно передать объект с кучей дополнительных опций, которые помогут найти нужный элемент. Например, если у вас на странице куча кнопок, а нужно найти конкретную.
Тут будет много опций для поиска по
🔸hidden: boolean
По умолчанию значение равно false - из-за этого в поиск не включаются "недоступные" элементы (display: none, aria-hidden, role=none).
🔸name: TextMatch
Для элемента формы - это текст лейбла, для кнопки - собственно текст кнопки. Также может использоваться значение атрибута
Что за тип такой
Функция принимает два аргумента: content: string (собственно текстовый контент, по которому производится поиск) и element (DOMElement) и должна вернуть true или false.
🔸description: TextMatch
Это для атрибута aria-describedby.
🔸selected: boolean
Отбор по значению атрибута aria-selected.
🔸busy: boolean
Отбор по значению атрибута aria-busy.
🔸checked: boolean
Отбор по значению атрибута aria-checked.
🔸pressed: boolean
Отбор по значению атрибута aria-pressed.
🔸suggest: boolean
Эта настройка для того, чтобы Testing Library предлагала вам запросы получше, чем тот, что написали вы.
🔸current: boolean | string
Отбор по значению атрибута aria-current.
🔸expanded: boolean
Отбор по значению атрибута aria-expanded.
🔸queryFallbacks: boolean
По умолчанию учитывается только первая роль каждого элемента, но есть поставить тут true, то будут приняты во внимание и запасные роли, если они есть.
🔸level: number
Уровень заголовка для роли
🔸value
Это для группы атрибутов
#тестирование #testinglibrary #документация
Итак, начнем с подробного разбора самого главного запроса в Testing Library:
byRole
. Это методы getByRole
, getAllByRole
, queryByRole
, queryAllByRole
, findByRole
, findAllByRole
.Документация: https://testing-library.com/docs/queries/byrole
Собственно роль
Первым параметром метод принимает собственно роль нужного элемента в виде строки. Это может быть
button
, heading
, switch
и так далее. Речь идет не об атрибуте
role
, а прежде всего о дефолтной (встроенной) роли элементов. Например, элемент button
имеет роль button
, даже без явного указания на это. Вот тут можно почитать про роли элементов.Опции
Вторым параметром можно передать объект с кучей дополнительных опций, которые помогут найти нужный элемент. Например, если у вас на странице куча кнопок, а нужно найти конкретную.
Тут будет много опций для поиска по
aria-
атрибутам.🔸hidden: boolean
По умолчанию значение равно false - из-за этого в поиск не включаются "недоступные" элементы (display: none, aria-hidden, role=none).
🔸name: TextMatch
Для элемента формы - это текст лейбла, для кнопки - собственно текст кнопки. Также может использоваться значение атрибута
aria-label
.Что за тип такой
TextMatch
? Это составной тип, который может быть обычной строкой, регулярным выражением или даже функцией.Функция принимает два аргумента: content: string (собственно текстовый контент, по которому производится поиск) и element (DOMElement) и должна вернуть true или false.
screen.getByText((content, element) => content.startsWith('Hello'))
🔸description: TextMatch
Это для атрибута aria-describedby.
🔸selected: boolean
Отбор по значению атрибута aria-selected.
🔸busy: boolean
Отбор по значению атрибута aria-busy.
🔸checked: boolean
Отбор по значению атрибута aria-checked.
🔸pressed: boolean
Отбор по значению атрибута aria-pressed.
🔸suggest: boolean
Эта настройка для того, чтобы Testing Library предлагала вам запросы получше, чем тот, что написали вы.
🔸current: boolean | string
Отбор по значению атрибута aria-current.
🔸expanded: boolean
Отбор по значению атрибута aria-expanded.
🔸queryFallbacks: boolean
По умолчанию учитывается только первая роль каждого элемента, но есть поставить тут true, то будут приняты во внимание и запасные роли, если они есть.
🔸level: number
Уровень заголовка для роли
heading
. Учитывает как семантику тега, так и атрибут aria-level
.🔸value
Это для группы атрибутов
aria-value
, например, aria-valuemin
, aria-valuetext
. Указывается в виде объекта:
screen.getByRole('spinbutton', { value: { min: 5, max: 10 }})
#тестирование #testinglibrary #документация
Testing-Library
ByRole | Testing Library
getByRole, queryByRole, getAllByRole, queryAllByRole, findByRole,
👍2
ByLabelText, ByPlaceholderText
Два запроса, предназначенных в основном для интерактивных элементов форм: поиск по тексту лейбла и по плейсхолдеру.
ByLabelText
Поле ввода может быть связано с лейблом разными способами:
- через атрибуты for и id
- через атрибут aria-labelledby
- если поле находится внутри label
- лейбл можно указать в атрибуте aria-label
Сигнатура:
Текст лейбла можно задать первым параметром в виде строки, регулярки или функции (TextMatch).
Вторым параметром идет объект настроект:
- selector - можно дополнительно указать селектор нужного элемента
- exact - если текст задан в виде строки, то этот параметр определяет должен ли поиск быть точным (по полной строке, с учетом регистра символов) или нет
- normalizer - по умолчанию Testing Library нормализует текст (убирает лишние пробелы). Можно передать собственный нормализатор
ByPlaceholderText
Очень похожий метод, только ищет элемент по тексту плейсхолдера. Менее предпочтительный, следует использовать только если элемент нельзя найти по лейблу или роли.
Сигнатура:
#тестирование #testinglibrary #документация #примерыкода
Два запроса, предназначенных в основном для интерактивных элементов форм: поиск по тексту лейбла и по плейсхолдеру.
ByLabelText
Поле ввода может быть связано с лейблом разными способами:
- через атрибуты for и id
- через атрибут aria-labelledby
- если поле находится внутри label
- лейбл можно указать в атрибуте aria-label
Сигнатура:
screen.getByLabelText(
text: TextMatch,
options: {
selector?: string = '*',
exact?: boolean = true,
normalizer?: NormalizeFn
}
)
Текст лейбла можно задать первым параметром в виде строки, регулярки или функции (TextMatch).
Вторым параметром идет объект настроект:
- selector - можно дополнительно указать селектор нужного элемента
- exact - если текст задан в виде строки, то этот параметр определяет должен ли поиск быть точным (по полной строке, с учетом регистра символов) или нет
- normalizer - по умолчанию Testing Library нормализует текст (убирает лишние пробелы). Можно передать собственный нормализатор
ByPlaceholderText
Очень похожий метод, только ищет элемент по тексту плейсхолдера. Менее предпочтительный, следует использовать только если элемент нельзя найти по лейблу или роли.
Сигнатура:
screen.getByPlaceholderText(
text: TextMatch,
options: {
exact?: boolean = true,
normalizer?: NormalizerFn
}
)
#тестирование #testinglibrary #документация #примерыкода
👍2