Заметки про React – Telegram
Заметки про React
3.78K subscribers
34 photos
8 videos
485 links
Короткие заметки про React.js, TypeScript и все что с ним связано
Download Telegram
Обработка ошибок с TypeScript

Для долговечности работы проекта крайне важно отлавливать и обрабатывать ошибки.

В TypeScript при обработке ошибки в try/catch тип ошибки всегда unknown:

try {
// --snip--
} catch (error) {
// error is unknown
}


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

В JavaScript много стандартных типов ошибок, например: ReferenceError, TypeError, SyntaxError. Также можно создавать свои собственные – пользовательские типы ошибок. При создании пользовательского типа ошибки расширяйтесь от стандартного класса Error, чтобы к ошибке прикреплялся стектрейс. Использование пользовательских ошибок позволяет более детально обрабатывать каждую ошибку и по разному реагировать на нее приложению. Пример:

export class ProjectError extends Error {
name: ErrorName;
message: string;
cause: any;

constructor({
name, message, cause
}: {
name: ErrorName;
message: string;
cause?: any
}) {
super();
this.name = name;
this.message = message;
this.cause = cause;
}
}

try {
throw new ProjectError({
name: "CREATE_PROJECT_ERROR",
message: "API Error"
});
} catch (error) {
if (error instanceof ProjectError) {
if (error.name === "CREATE_PROJECT_ERROR") {
toast(error.message);
}
}
}


https://engineering.udacity.com/handling-errors-like-a-pro-in-typenoscript-d7a314ad4991
👍8
Абсолютная абстракция форм в React

Гайд по созданию абстракций для форм от Брендана Аллана. В примерах для работы с формами автор использует react-hook-form, TypeScript и Zod.

Преимущество Zod в том, что он полностью поддерживает TypeScript, также есть утилитарный тип TypeOf для выведения типа из схемы. RHF поддерживает Zod через resolver.

Пример кастомного хука формы:

import { useForm, UseFormProps } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { TypeOf, ZodSchema } from 'zod';

interface UseZodFormProps<Z extends ZodSchema>
extends Exclude<UseFormProps<TypeOf<Z>>, 'resolver'> {
schema: Z;
}

export const useZodForm = <Z extends ZodSchema>({
schema,
...formProps
}: UseZodFormProps<Z>) =>
useForm({
...formProps,
resolver: zodResolver(schema),
});


https://www.brendonovich.dev/blog/the-ultimate-form-abstraction
👍13👎2
Практики композиции функций в React

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

- Используйте HoC для абстракции общей функциональности. Оборачивая в HoC мы можем добавить дополнительное поведение или пропсы, не изменяя оригинальный компонент.
- Объединяйте несколько HoC, используя композицию: compose(withFoo, withBar, withBaz)(Component).
- Один HoC делает только одну работу, используйте кастомные хуки для хранения состояния.
- Используйте техники функционального программирования, например, каррирование.

Пример реализации HoC, который абстрагирует логику получения данных о пользователе:

import { useEffect, useState } from 'react';

function useFetchUsers(url) {
const [users, setUsers] = useState([]);
useEffect(() => {
async function fetchData() {
const res = await fetch(url);
const data = await res.json();
setUsers(data.users);
}
fetchData();
}, [url]);
return users;
}

function withFetchUsers(Component) {
return function WrappedComponent(props) {
const users = useFetchUsers(props.url);
return <Component {...props} users={users} />;
}
}


https://medium.com/@rivoltafilippo/function-composition-in-large-react-applications-best-practices-and-real-world-examples-805aba8d37b1
👍6👎5
Библиотеки для визуализации

Подборка новых библиотек для визуализации данных для React:

visx

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

export default function Bars() {
// --snip--
const yScale = useMemo(
() =>
scaleLinear<number>({
range: [yMax, 0],
round: true,
domain: [0, Math.max(...data.map(getLetterFrequency))],
}),
[yMax],
);

return (
<noscript width={width} height={height}>
<GradientTealBlue id="teal" />
<Group top={verticalMargin / 2}>
{data.map((d) => {
// --snip--
const barHeight = yMax - (yScale(getLetterFrequency(d)) ?? 0);
const barX = xScale(letter);
return (
<Bar
key={`bar-${letter}`}
x={barX}
y={barY}
// --snip--
/>
);
})}
</Group>
</noscript>
);
}



mafs

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

function LineSegmentExample() {
const point1 = useMovablePoint([-1, -1])
const point2 = useMovablePoint([2, 1])

return (
<Mafs viewBox={{ y: [-1, 1] }}>
<CartesianCoordinates />
<Line.Segment
point1={point1.point}
point2={point2.point}
/>
{point1.element}
{point2.element}
</Mafs>
)
}
👍41
Все что вам нужно знать про конкурентный рендеринг

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

Для того чтобы активировать конкурентный рендеринг, нужно использовать либо переходы – useTransition или просто startTransition, либо отложенные значения – useDeferredValue. Эти API позволяют пометить обновления стейта низким приоритетом. Обновления стейта, которые объявляются вне фич конкурентного рендеринга, помечаются как обновления с высоким приоритетом.

Хенрику Йужи подробно разобрал API конкурентного рендеринга и описал крайние кейсы использования. Например, нужно понимать, что React рендерит в однопоточной среде. И если он начал рендерить компонент, то он будет рендерить его до конца без прерываний. Только по окончании рендера компонента React проверит, есть ли какая-либо высокоприоритетная задача в пуле задач. Как следствие, следует избегать задач, интенсивно использующих ЦП, которые выполняются в одном компоненте, а не распределены по нескольким компонентам.

https://blog.codeminer42.com/everything-you-need-to-know-about-concurrent-react-with-a-little-bit-of-suspense/
👍5🔥2
Почему мои Jest тесты такие медленные?

Запуск тестов через Jest может занять гораздо больше времени, чем хотелось бы. Чтобы разобраться в причинах медленной работы тестов, стоит понять на что Jest тратит время. При выполнении команды запуска тестов происходит следующее:

- Загрузка окружения (`jest-environment-jsdom`), построение графа зависимостей между файлами, загрузка плагинов и запуск дополнительных потоков.
- Заполнение кэша. Первый запуск будет длиться чуть дольше, т.к. Jest необходимо заполнить кэш. Процесс будет идти еще дольше, если происходит транспиляция TypeScript.
- Загрузка файла теста. Перед запуском теста необходимо загрузить или замокать все зависимости, указанные в файле теста и в файле setupTests.ts. Этот шаг можно оптимизировать.
- Выполнение теста.

Можно использовать Chrome DevTools, чтобы записать профиль производительности работы Jest и посмотреть, на что уходит больше всего времени. Автор подметил несколько основных кейсов, которые делают тесты медленными:
- Баррель файлы. Если вы импортируете компонент из библиотеки, то пишите полный путь к компоненту, а не используйте баррель файл. Баррель файл – это index.ts, который реэкспортирует все экспорты. Когда Jest встречает на своем пути баррель файл, то он загружает все экспорты, объявленные в нем.
- Проверьте setupTests.ts, он запускается перед каждым тестовым файлом.
- Если используете TypeScript, то уберите проверку типов при запуске тестов.
- Проверьте настройки Jest, возможно включены неэффективные для локальной разработки параметры запуска тестов.

https://blog.bitsrc.io/why-is-my-jest-suite-so-slow-2a4859bb9ac0
👍6
Ключ к хорошему дизайну компонента – эгоизм

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

Даниэль Ющик в статье на Smashing Magazine написал подробный гайд с примерами по созданию компонентов для дизайн системы.

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

- HTML управляет дизайном компонента. При разработке абстрактного компонента, наприме, Button не стоит отклоняться от нативного поведения базового элемента. Если у нативного элемента есть проп children, то и у абстрактного он должен быть. Иначе это делает компонент более жестким к изменениям, и новым разработчикам придется менять ментальную модель для работы с данным компонентом.

- children заботится сам о себе. Чем больше мы будем пытаться стилизовать содержимое компонента, тем сложнее это будет поддерживать и дорабатывать. Компонент должен стилизовать только себя, а не свое содержимое.

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

<Modal>
<Modal.CloseButton />
<Modal.Header> ... </Modal.Header>
<Modal.Main> ... <Modal.Main>
</Modal>


https://www.smashingmagazine.com/2023/01/key-good-component-design-selfishness/
👍9
Tamagui – универсальный UI kit

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

Библиотека tamagui состоит из ядра @tamagui/core и компилятора @tamagui/static. Компилятор предназначен для генерации и оптимизации CSS стилей для веба. Поддерживает кастомизации, можно изменить тему любого компонента.

https://tamagui.dev/
👍5👎1
Разбираемся в TypeScript с помощью теории множеств

Владимир Клепов написал подробный гайд про устройство типов в TypeScript. Для наглядности применяются определения из теории множеств: объединение A ∪ B , пересечение ​​A ∩ B, разность A \ B , подмножество и надмножество. Например:
- A extends B можно выразить как “A является подмножеством B”.
- Тип объединения | и пересечения & является объединением и пересечением двух множеств.
- Exclude<A, B> можно назвать разностью двух множеств, но с условием что A и B являются объединением.
- never – пустое множество. A & never = never и A | never = A.

Одним из интересных примеров можно выделить то, что TypeScript позволяет объявлять такие выражения: const x: {} = 9;. Это происходит из-за того, что тип {} не значит то же самое, что и пустой объект в JS. Это тип, в котором можно получить доступ к свойствам, но не важно к каким. Чтобы было понятнее, еще один похожий пример валидного выражения: const x: { toString(): string } = 9;. Это связано с тем, что TypeScript под капотом видит примитив как объект. Поэтому TypeScript позволяет выполнять такие выражения, т.к. благодаря автобоксингу JS выражение валидно и можно вызвать x.toString().

https://blog.thoughtspile.tech/2023/01/23/typenoscript-sets/
👍12
Astro 2.0

Вышла новая мажорная версия фреймворка Astro 2.0. Основные изменения:

Коллекция контента.
Это подход к организации контента, с помощью которого можно писать типобезопасный код в Markdown/MDX разметке. Коллекция контента – это любая папка внутри папки src/content, например “blog”, “newsletter”, "products". Внутри нее размещаются Markdown/MDX файлы с разметкой и полями. В корне src/content создается config файл, из которого экспортируется объект, где ключ - это название папки-коллекции, а значение - описание. В описании коллекции указывается Zod схема полей в разметке. После чего Astro проверяет поля в файлах разметки на правильность написания, а также предоставляет типобезопасное API запросов получения контента.

// src/content/config.ts
// –snip–
const blogCollection = defineCollection({ schema: blogSchema });
export const collections = {
'blog': blogCollection
};

// src/pages/work.astro
import { getCollection } from 'astro:content';
const allBlogPosts = await getCollection('blog');
// –snip–


Гибридный рендеринг
В Astro появилась возможность пререндера конкретной страницы. Эти страницы рендерятся во время сборки приложения, что приводит к экономии времени при загрузке страницы. Чтобы страница рендерилась во время сборки, нужно добавить запись:

export const prerender = true;


Другие изменения:
- Редизайн страницы ошибок
- Оптимизация Hot Module Reloading (HMR)
- Используется Vite 4.0

https://astro.build/blog/astro-2/
👍4
Новая документация React готова на 99%

По сообщению Дэна Абрамова для того, чтобы вывести новую документацию из беты, осталось сделать сайт лендинга.

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

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

https://beta.reactjs.org/
🔥33👍91👎1
Релиз TypeScript 5.0 Beta

На днях вышел релиз мажорной версии TypeScript 5.0 Beta. В этом релизе много новых фич. Кратко о некоторых фичах релиза:

Новый стандарт декораторов

В TS уже были декораторы, и они были доступны под флагом. В версии 5.0 декораторы будут доступны без флага, а те декораторы, которые были доступны под флагом, объявлены как легаси. Пример использования новых декораторов:

function loggedMethod<This, Args extends any[], Return>(
target: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
const methodName = String(context.name);

function replacementMethod(this: This, ...args: Args): Return {
console.log(`LOG: Entering method '${methodName}'.`)
const result = target.call(this, ...args);
console.log(`LOG: Exiting method '${methodName}'.`)
return result;
}

return replacementMethod;
}

class Person {
// --snip--

@bound
@loggedMethod
greet() {
console.log(`Hello, my name is ${this.name}.`);
}
}


Для работы с декораторами добавлен новый тип ClassMethodDecoratorContext, который моделирует объект контекста, который принимают декораторы метода.
У метода может быть несколько декораторов. В примере сначала выполнится @loggedMethod, а потом @bound.
Декораторы можно использовать не только у метода, но и у класса и его полей.

const как параметр типа

В TS есть утверждение as const, которое делает выражение иммутабельным и говорит компилятору, что тип больше не будет расширяться.
В TS 5.0 можно добавить const как модификатор к объявлению типа параметра. Пример:

type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
return arg.names;
}

// readonly ["Alice", "Bob", "Eve"]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });


При этом использование модификатора const не требует ограничения типа в виде readonly. Если не использовать readonly, то результат будет другим:

declare function fnBad<const T extends string[]>(args: T): void;

// 'T' будет 'string[]', а не 'readonly ["a", "b", "c"]'
fnBad(["a", "b" ,"c"]);


Несколько конфиг файлов в extends

Появилась возможность расширять tsconfig от несколько файлов:

{
"compilerOptions": {
"extends": ["a.json", "b.json", "c.json"]
}
}


В этом случае b.json расширяет a.json, а c.json расширяет b.json, а текущий конфиг расширяет c.json.

https://devblogs.microsoft.com/typenoscript/announcing-typenoscript-5-0-beta/
👍13
PR с заменой CRA на Vite

В репозитории reactjs.org стал популярным PR с предложением использовать Vite вместо CRA.

На это предложение ответил Дэн Абрамов. В своем комментарии он рассказал историю CRA и ее цели. Основной посыл заключается в том, что команда React не хочет забрасывать CRA, но и не хочет его рекомендовать. Важно создавать новые приложения с поддержкой SSG/SSR. В CRA же нет поддержки SSG/SSR.

Одной из целей развития CRA видят превращение его в CLI лаунчер: давать на выбор фреймворк для создания приложения: Next.js, Remix, CRA и т.д. Также возможно в будущем react-noscripts для сборки приложения перейдет на Vite.

https://github.com/reactjs/reactjs.org/pull/5487#issuecomment-1409720741
👍12
React рекурсивно ре-рендерит дочерние компоненты, но есть нюанс

Алекс Сидоренко в своем блоге сделал наглядную визуализацию того, как разделение компонентов и использование совместного размещения состояния (state colocation) позволяют предотвращать лишний рендер.

https://alexsidorenko.com/blog/react-render-children-prop/
👍9🔥21
React.js: Документальный фильм

Вышел документальный фильм про React. В нем рассказывается история создания библиотеки от самих разработчиков. В фильме снялись Том Оккино, Кристофер Чедо, Пит Хант, Себастьян Маркбоге, Дэн Абрамов и многие другие.

https://www.youtube.com/watch?v=8pDqJVdNa44
🔥8👍4
Гайд по обработке ошибок в React

В блоге Нади Макаревич вышел гайд по обработке ошибок в React.

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

Для того, чтобы предотвратить такие ситуации и отловить ошибку во время фазы жизненного цикла React, есть Error Boundaries. У него есть минусы: если возникла ошибка в колбеке или в асинхронном коде, то такая ошибка не дойдет до Error Boundaries:

const Component = () => {
useEffect(() => {
// Будет обработан ErrorBoundary
throw new Error('Destroy everything!');
}, [])

const onClick = () => {
// Не будет обработан ErrorBoundary
throw new Error('Hulk smash!');
}

useEffect(() => {
// Если возникнет ошибка, то не будет обработан ErrorBoundary
fetch('/bla')
}, [])

return <button onClick={onClick}>click me</button>
}

const ComponentWithBoundary = () => {
return (
<ErrorBoundary>
<Component />
</ErrorBoundary>
)
}


Чтобы направить ошибку из колбека или асинхронного кода в ErrorBoundary, можно перекинуть ошибку в React используя трюк:

const onClick = () => {
try {
//–snip–
} catch (e) {
// вызвать обновление стейта с выбросом ошибки
setState(() => throw e);
}
}


Чтобы самостоятельно не изобретать колесо, рекомендуется использовать библиотеку react-error-boundary. В ней уже решены основные проблемы, возникающие при обработке ошибок.

https://www.developerway.com/posts/how-to-handle-errors-in-react
🔥7👍5
Переезд с Webpack на Vite для микрофронтенда

История переезда проекта со сборщика Webpack на Vite. Особенность проекта в том, что это микрофронтенд и используется SingleSPA и SystemJS importmap. В свою очередь Vite использует нативные ES6 модули в dev режиме для быстрой разработки. Поэтому использование SystemJS будет только замедлять сборку. Чтобы решить проблему, нужно перейти на нативный importmap и отказаться от SystemJS.

https://habr.com/ru/company/dododev/blog/715988/
👍41🔥1
Что мне нравится в Astro

Дэйв Рэмси сравнивает Astro с Next.js и Gatsby. Автор заметил, что Next.js не очень подходит для сайта-блога. При посещении сайта пользователю приходится загружать слишком много JS, чтобы просто прочитать статью. При переводе сайта с Next.js на Astro автор заметил некоторые вещи, которые нравятся больше, чем в Next.js и Gatsby.

- Поддерживает любой источник данных

Astro никак не ограничивает способ получения данных. Он поддерживает ключевое слово await в шаблоне, поэтому можно записать fetch в переменную и использовать ее в шаблоне:

---
const response = await fetch('https://an-endpoint.com/api');
const data = await response.json();
---
<p>{data.a_property}</p>


- Есть готовые интеграции

В Astro есть гайды по генерации sitemap, RSS ленты и оптимизации изображений. Также есть готовые модули для работы с СЕО.

- Сложно написать непроизводительное приложение

По умолчанию в Astro не создается JS для клиента. Создавать Astro компоненты легко, особенно после опыта написания React компонентов.

- Большая часть API построена на веб-стандартах

Большая часть низкоуровневого API Astro основана на веб-стандартах, поэтому его проще изучить. Например, Astro.request является объектом Request из Fetch API.

https://www.macarthur.me/posts/what-i-like-about-astro
👍4🔥1
Гайд по сужению типов в TypeScript

В своем блоге Владимир Клепов написал подробный гайд по сужению типов в TypeScript. Сужение типов полезно, когда получаешь данные извне: читаешь localStorage, парсишь URL или читаешь пользовательский ввод. Также сужение типа применяется, когда мы расширяем API для удобства, например, вызов функции mount('#mount’) и mount(document.querySelector('#mount’)).

В статье рассматриваются несколько методов безопасного сужения типов:
- Использование нативных JS операторов, таких как typeof, === и других.
- Использование type guard функций. Они полезны, но, как ни странно, небезопасны для типов.
- Написать функцию приведения типа, например (x: unknown) => number.

https://blog.thoughtspile.tech/2023/01/31/typenoscript-safe-narrow/
👍6🔥31
Краткое введение в Tauri

Фернандо Дольо рассказывает о создании простого нативного приложения с использованием Tauri и React. В гайде также есть пример взаимодействия React приложения с Tauri через Rust.

Tauri – это набор инструментов для создания нативных приложений с использованием JS, альтернатива Electron. Написан на Rust, поэтому для взаимодействия с ОС нужно писать код на Rust. В отличие от Electron не использует Chromium для рендера приложения, а использует нативный WebView операционной системы. Плюс такого подхода в том, что размер приложения минимальный. На сайте написано, что размер приложения может быть меньше 600KB. Минус в том, что у каждой ОС свой WebView, поэтому будет отличаться дизайн нативных UI элементов.

https://blog.bitsrc.io/how-to-build-desktop-apps-using-react-a-quick-introduction-to-tauri-63fcad43914e
👍5🔥1
useSignal() — это будущее веб-фреймворков

Signal — это способ хранения состояния приложения, аналогичный useState() в React.

Ключевое различие между Signal и React стейтом в том, что Signal возвращает геттер и сеттер, а React стейт возвращает значение и сеттер.

Miško Hevery сделал сравнение Signal и хуков в React. Signal является реактивным, т.е. он должен отслеживать, кто заинтересован в состоянии, и уведомлять подписчиков об изменении состояния. Это достигается с помощью наблюдения за контекстом, в котором вызывается геттер, создающий подписку.

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

https://www.builder.io/blog/usesignal-is-the-future-of-web-frameworks
👍14👎1