Заметки про React – Telegram
Заметки про React
3.78K subscribers
34 photos
8 videos
485 links
Короткие заметки про React.js, TypeScript и все что с ним связано
Download Telegram
Архитектура веб-приложений patterns.dev

Адди Османи и Лидия Холли представили книгу с описанием шаблонов проектирования приложений. Книгу также можно почитать прямо на сайте проекта.

В ней есть как стандартные шаблоны проектирования, такие как Singleton, Factory и т.д., так и шаблоны рендеринга, например, рассматривается React Server Components.

В книге есть три раздела: шаблоны проектирования, рендеринга и производительности. В главах есть примеры реализации шаблонов на React или на ванильном JS.

https://www.patterns.dev/
Ленивая инициализация useRef

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

Есть несколько подходов к реализации данной техники:

- Установка значения рефа в useEffect. Минус в том, что значение рефа не будет доступно в первый рендер, в useLayoutEffect, а также в дочерних useEffect.

- Кастомный хук с инициализацией на месте. Минус подхода в том, что функция инициализации выполняется даже если значение рефа было не нужно. Также инициализация блокирует первый рендер.

const none = {};
function useLazyRef(init) {
const ref = useRef(none);
if (ref.current === none) {
ref.current = init();
}
return ref;
}


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

const ref = useState(() => ({ current: init() }))[0];


- Создать кастомный хук с полями аксессорами (accessor properties). Если у объекта задать геттер и сеттер на поле current и внутри использовать реф, то получится правильная ленивая инициализация рефа. Объект будет проинициализирован только при доступе к полю current.

https://thoughtspile.github.io/2021/11/30/lazy-useref/
👍2
Итоги 2021 года

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

- Релиз фреймворка Remix в open source.
- Адди Османи и Лидия Холли представили проект с примерами использования паттернов проектирования patterns.dev.
- Вышла новая версия React Router 6.0.
- Прошла конференция React Conf 2021 (реплей).
- React 18 перешел в статус бета.
- Вышла новая мажорная версия Create React App 5.0.
- Опасности гидратации в React - читать.
- Гайд по разработке React приложений - читать.
- Как работает обработка ошибок в React - читать.

Всех с наступающим 2022 годом! 🎅☃️🎁
👍5
Использование React Server Components в Next.js 12

React Server Components (RSC) – это компоненты, которые выполняются на сервере, и на клиент передается уже отрендеренный компонент в формате JSON.
Рассмотрим основные особенности серверных компонентов. RSC не увеличивает размер бандла приложения, т.к. выполняются только на сервере. Компонент имеет доступ к бэкенду и базе данных. В серверных компонентах нельзя использовать состояние и эффекты, поэтому не поддерживаются хуки useState, useEffect и т.д.

Попробовать RSC пока что можно только вместе с каким-нибудь фреймворком, например, Next.js 12. В настройках конфига Next.js нужно включить экспериментальные фичи для поддержки конкурентного режима и серверных компонентов:

// next.config.js
module.exports = {
experimental: {
concurrentFeatures: true,
serverComponents: true,
},
}

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

В серверных компонентах использование Suspense позволяет не ждать загрузки всех данных для ответа клиенту. Suspense создает “слоты” в возвращаемом JSON. При получении данных и рендере компонентов внутри Suspense, сервер стримит JSON новых компонентов клиенту, который вставляет их в нужные слоты.

Не стоит путать серверные компоненты и техники пререндера контента, такие как Server-side rendering (SSR) или Static site generation (SSG). RSC позволяет отрендерить на сервере только часть компонентов на странице, а не всю страницу целиком.

https://blog.logrocket.com/react-server-components-nextjs-12/

демка: https://next-news-rsc.vercel.sh/ (github)
🔥10
Современные сборщики 2022

Собрал небольшую коллекцию инструментов-сборщиков, к которым стоит обратить внимание в 2022 году.

SWC – swc.rs
Предназначен для минификации, бандлинга и компиляции исходного кода веб-приложения.
Используется в Next.js 12, Parcel 2 и Deno. В Next.js заменяет Babel для бандлинга и Terser для минификации. В приложениях сборка ускорилась в 5 раз, а fast refresh в 3 раза. Компиляция кода в 17 раз быстрее чем Babel. Написан на Rust.
Поддерживает из коробки TypeScript, React.
Есть возможность интеграции в webpack для компиляции кода через ​​swc-loader. Пример.

Vite – vitejs.dev
Использует Rollup для сборки приложений и esbuild для предварительной сборки зависимостей. В Vite очень быстро работает dev-сервер и HMR. В dev режиме Vite не собирает бандл, а использует нативные ES модули. Есть готовые плагины для интеграции с React. Поддерживает TypeScript из коробки. Пример.

Romerome.tools
Швейцарский нож в сфере веб-разработки. Из коробки должен будет уметь бандлить, форматировать, компилировать, минифицировать, тестировать и т.д. Также будет поддержка TypeScript и React.
Проект изначально делался на TypeScript, но после был полностью переписан на Rust. Пока что частично работает только линтинг.

esbuildesbuild.github.io
Супер быстрый сборщик, написанный на Go. Из коробки поддерживает TypeScript и React, умеет делать минификацию. Есть возможность запуска dev-сервера: esbuild --bundle src/index.js --outfile=www/main.js --servedir=www
👍14🔥2
Blitz – фулстек фреймворк для React

Фреймворк Blitz сделан на базе Next.js, его ключевой особенностью является подход “Zero API”. Этот подход добавляет абстрактный слой работы с данными, позволяя избавиться от работы с REST или GraphQL. При разработке компонентов можно напрямую вызывать функции бэкенда. Во время сборки эти вызовы будут заменены на HTTP запросы к API, а само API будет сгенерировано автоматически.

Этот подход хорошо сочетается с TypeScript. При передаче параметров в функцию бэкенда будет происходить автоматическая проверка типов.

Функции бэкенда можно использовать по разному. В них можно вызывать напрямую изменения в БД, реализовать BFF, либо делать какие-то вычисления. По умолчанию для работы с БД предустановлена Prisma. Из коробки реализована поддержка авторизации пользователя по имейл/паролю.

https://blitzjs.com/
👎2🔥2
tRPC – замена REST и GraphQL

Заметил в Blitz интересный подход по работе с API и обнаружил библиотеку tRPC, которая делает точно так же, но не привязана к конкретному фреймворку.

Библиотека реализует технологию RPC по передаче данных. Все запросы делятся на получение и изменение данных и отправляются на один URL. Ответ на запрос приходит в формате JSON, из которого можно получить состояние ответа, сообщение об ошибке и данные. Если одновременно необходимо отправить несколько запросов, то их можно забатчить – объединить в один.

Главная особенность tRPC в том, что при написании типов параметров и ответов на сервере их можно использовать на клиенте. Делается это импортом типов, поэтому в клиентский бандл серверная часть кода не попадает.

Для React в tRPC есть готовые хуки useQuery и useMutation для получения и изменения данных. Также есть готовый плагин для Next.js для поддержки SSR и SSG.


// server/index.ts
import * as trpc from '@trpc/server';
import { z } from 'zod';

const appRouter = trpc
.router()
.query('getUser', {
input: (val: unknown) => {
if (typeof val === 'string') return val;
throw new Error(`Invalid input: ${typeof val}`);
},
async resolve(req) {
req.input; // string
return { id: req.input, name: 'Bilbo' };
},
})
.mutation('createUser', {
// validate input with Zod
input: z.object({ name: z.string().min(5) }),
async resolve(req) {
// use your ORM of choice
return await UserModel.create({
data: req.input,
});
},
});

export type AppRouter = typeof appRouter;


https://trpc.io/
👍7🔥4👎2
Обзор React Router v6

Тайлер МакГиннис написал подробный обзор про React Router v6.

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

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

https://ui.dev/react-router-tutorial/
👍6👎2
Решаем проблему проп дриллинга

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

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

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

function App() {
const [data, setData] = useState("some state");

return (
<ParentComponent>
<ComponentOne>
<ComponentTwo data={data} />
</ComponentOne>
</ParentComponent>
);
}


https://blog.logrocket.com/solving-prop-drilling-react-apps/#container-components
👍8
Хочу провести небольшой опрос по инструментам тестирования на React проектах:
Какой test runner используете на проекте для юнит и интеграционного тестирования?
Anonymous Poll
75%
Jest
5%
Mocha
3%
Ava
2%
Jasmine
6%
Другое
20%
Не пишет тесты
Какие библиотеки для тестирования используете на проекте?
Anonymous Poll
54%
Testing Library
15%
Enzyme
30%
Cypress
11%
Playwright
10%
Selenium
13%
Puppeteer
15%
Другое
👍2
Условные выражения в JSX

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

- Рендеринг по числовому условию. Конструкция вида {gallery.length && <Gallery slides={gallery}>} может отрендерить в DOM число 0. Если gallery.length будет равен 0, то оно как falsy значение будет сразу возвращено из конструкции &&. Чтобы исправить проблему, можно использовать тернарный оператор: {gallery.length ? <Gallery slides={gallery} /> : null}.

- Условное И (&&) имеет больший приоритет, поэтому в конструкциях где имеются И (&&) и ИЛИ (||) используйте скобки для определения приоритета. Например: {cond1 || cond2 && <JSX />} тоже самое что и {cond1 || (cond2 && <JSX />)}, поэтому пишите явно {(cond1 || cond2) && <JSX />}.

- Не зацикливайтесь на тернарных операторах. Если есть вложенные тернарные операторы, то это плохой знак. Вместо вложенных тернарных операторов попробуйте использовать логическое И (&&) или функцию с if/else:

{isEmoji
? <EmojiButton />
: isCoupon
? <CouponButton />
: isLoaded && <ShareButton />}

Код выше можно заменить на функцию:

const getButton = () => {
if (isEmoji) return <EmojiButton />;
if (isCoupon) return <CouponButton />;
return isLoaded ? <ShareButton /> : null;
};

- Будьте аккуратны с props.children в условиях. В props.children может быть пустой массив, фрагмент, одиночный элемент. Поэтому условие {props.children && <div>{props.children}</div>} может привести к багам.

- Одинаковые элементы будут обновляться. Например, код {hasItem ? <Item id={1} /> : <Item id={2} />} будет эквивалентен коду <Item id={hasItem ? 1 : 2} />. Это значит, что при изменении hasItem компонент Item не будет размонтироваться и монтироваться заново, вместо этого будет обновление пропсов.

https://thoughtspile.github.io/2022/01/17/jsx-conditionals/
👍15
React server components под капотом

Чан Ву рассказал про внутреннее устройство React server components (RSC) — "How React server components work: an in-depth guide".

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

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

#react #internals

https://blog.plasmic.app/posts/how-react-server-components-work/
👍3
Исправление ошибок гидратации

Бен Илегбоду рассказывает, как исправить проблемы гидратации в React приложениях.

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

Один из способов решения подобных проблем – использование эффектов, о котором подробно рассказал Бен Илегбод в своей статье.

https://www.benmvp.com/blog/handling-react-server-mismatch-error/
👍5
Проблемы в типизации children

Рассмотрим пример компонента:

const Card: React.FC = props => { ... }

export default function App() {
const randomObject = {};
return (
<div className="App">
<Card>{randomObject}</Card>
</div>
);
}

Со стороны TypeScript в данном коде нет ошибок, компонент скомпилируется. Но при запуске приложения будет ошибка со стороны React: Objects are not valid as a React child (found: object with keys {}).
Проблема в том, что неверно написаны типы для React.ReactNode. Если посмотреть в исходники типов, то можно увидеть, что ReactNode расширяется от ReactFragment, который разрешает использование объекта {}.

interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

При разработке компонентов, которые принимают children, стоит учитывать ошибку в типах React. Вариант решения проблемы – создать свой тип ReactNode, который не допускает использование объекта.

https://fettblog.eu/react-types-for-children-are-broken/
👍8
Лучшие практики React Testing Library

Бен Илегбоду делится лучшими практиками при работе с библиотекой для тестирования Testing Library. Кратко о них:

- Используйте user-event вместо fireEvent для взаимодействия с элементами в компоненте. Библиотека Testing Library экспортирует fireEvent, но её API более низкоуровневое. Гораздо проще и читабельнее будет использование user-event.

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

- Используйте screen вместо деструктуризации render. Эта практика избавит от постоянного изменения объекта деструктуризации по мере добавления или удаления нужных нам запросов.

- Правильно используйте waitFor. Не кладите в колбек waitFor вызовы функций сайд-эффектов, например, клик по кнопке или ввод текста. Внутри waitFor должно быть только утверждение (expect). Связано это с тем, что waitFor вызывает функцию колбека несколько раз, пока утверждение внутри него не станет верным или закончится время на проверку.

Большинство случаев поддержки лучших практик можно добиться установкой правил ESLint для Testing Library, например, eslint-plugin-testing-library.

https://www.benmvp.com/blog/react-testing-library-best-practices/
👍7
Новая документация React

React запустил бету версию сайта документации beta.reactjs.org. Пока что она в процессе наполнения и не все разделы сайта готовы. Но уже сейчас видно, чем новая документация будет отличаться от старой: будет больше интерактивных примеров, добавятся задачи на понимание принципов работы React. Также во всех примерах и объяснениях будут использоваться хуки, а не классы.

https://beta.reactjs.org/
👍16👎1
Тестирование React компонентов с помощью Testing Library

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

https://dev.to/ohdylan/react-component-testing-54ie
👍5
Кастомные хуки и производительность приложения

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

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

https://www.developerway.com/posts/why-custom-react-hooks-could-destroy-your-app-performance
👍6