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

Вышла новая мажорная версия библиотеки create-react-app. Основные изменения:

- webpack 5
- Jest 27
- PostCSS 8
- ESLint 8
- Улучшения fast refresh
- Поддержка Tailwind

Для миграции нужно просто установить новую версию react-noscripts. Если будут ошибки, то нужно удалить node_modules и переустановить модули.

Как и любой мажорный релиз, новый react-noscripts содержит критические изменения. Например, была удалена поддержка Node.js 10 и 12 версии. В остальном, обещают что критические изменения не будут аффектить каждого пользователя.

https://github.com/facebook/create-react-app/releases/tag/v5.0.0
Опасности гидратации в React

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

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

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

function Navigation() {
const [hasMounted, setHasMounted] = React.useState(false);

React.useEffect(() => {
setHasMounted(true);
}, []);

if (!hasMounted) {
return null;
}

const user = getUser();

...
}


https://www.joshwcomeau.com/react/the-perils-of-rehydration/
Использование Rust в React приложениях

Джош Финни рассказывает, как использовать Rust в React приложениях. Для возможности использования Rust в вебе необходимо использовать модуль wasm-bindgen. Этот модуль генерирует биндинги для использования Rust программ через Wasm в вебе.

Использование Rust, как и например C, C++ или Go, через WebAssembly обеспечивает скорость выполнения кода близкой к нативной. WebAssembly также дает возможность переносить сложные вычисления за пределы JavaScript.

https://www.joshfinnie.com/blog/using-webassembly-created-in-rust-for-fast-react-components/

🇷🇺 https://habr.com/ru/company/timeweb/blog/594967/
Эффективный рендер больших списков

При работе с большими списками данных при проблемах с производительностью стоит обратить внимание на виртуализацию списка. Одна из таких библиотек для React – react-window. Библиотека умеет делать виртуализацию списка и сетки элементов с фиксированным или динамическим размером.

Виртуализация списка позволяет улучшить производительность страницы в целом, уменьшив количество отрендендеренных элементов. При скролле списка рендерятся в DOM только те элементы, которые находятся в поле видимости для пользователя. Если элемент покидает поле видимости, то он удаляется из DOM.

https://web.dev/virtualize-long-lists-react-window/
Архитектура веб-приложений 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