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

Люк Шафер в своем блоге сделал туториал по созданию собственной библиотеки наподобие Сигнал. Получилась упрощенная версия без оптимизаций, но достаточная, чтобы понять саму концепцию.

Сигнал в основном используется в SolidJS и также был добавлен в Angular, Preact и Qwik. Сигнал можно использовать и в React, используя @preact/signals-react.

Пример использования:

export function Counter() {
const [count, setCount] = createSignal(0);

return <button onClick={() => setCount(count() + 1)}>{count()}</button>
}


https://www.lksh.dev/blog/writing-your-own-reactive-signal-library/
👍7
Увеличиваем скорость работы Tanstack Table быстрее в 1000 раз

В блоге Jp Camara статья с разбором проблемы с производительностью библиотеки ​​Tanstack Table. Проблема была в том, что при использовании таблицы с группировкой столбцов, рендеринг 50к строк занимал 30-40 секунд. Использовать браузерный профайлер было невозможно из-за слишком медленной работы приложения. Поэтому для оценки скорости рендера и поиска проблемных мест использовался console.time.

Проблема была обнаружена внутри функции группировки библиотеки Tanstack Table:

function groupBy(rows, columnId) {
const groupMap = new Map<any, Row<TData>[]>()

return rows.reduce((map, row) => {
const resKey = `${row.getValue(columnId)}`
const previous = map.get(resKey)
if (!previous) {
map.set(resKey, [row])
} else {
map.set(resKey, [...previous, row])
}
return map
}, groupMap)
}


Здесь использовался спред оператор […previous, row] для конкатенации текущей строки в конец массива. Так как он использовался внутри другого цикла reduce, то сложность функции группировки была O(n^2). Поэтому для 50к строк выполнялось 2,5 миллиарда итераций.

https://jpcamara.com/2023/03/07/making-tanstack-table.html
🔥4👍1
Modern.js – фреймворк от ByteDance

Modern.js - это фреймворк на React от ByteDance. Возможно, вы не слышали о нем, но это полноценный фреймворк на React, у которого уже вышла 2ая версия, и есть готовая экосистема инструментов.

Фреймворк похож на Next.js и Remix. Роутинг на основе структуры в файловой системе, и есть файлы layout и page для рендера страниц. Для загрузки данных используются файлы layout.loader.ts и page.loader.ts, в которых экспортируются функции получения данных. Для получения данных в компоненте страницы используется хук useLoaderData.

// page.loader.ts
export type ProfileData = {
/* some types */
};

export default async (): Promise<ProfileData> => {
const res = await fetch('https://api/user/profile');
return await res.json();
};

// page.tsx
import { useLoaderData } from '@modern-js/runtime/router';
import type { ProfileData } from './page.loader.ts';

export default function UserPage() {
const profileData = useLoaderData() as ProfileData;
return <div>{profileData}</div>;
}


Другие возможности:
- Создание BFF. Для этого нужно добавить BFF плагин в конфиг, и можно определять свои функции в папке /api.
- Кастомизация веб-сервера. Можно добавить обработку аутентификации пользователя, пререндеринг скелетона приложения и т.д.
- Встроенная поддержка микро-фронтенда. Через конфиг можно настроить работу нескольких фронтенд приложений.
- Есть встроенный стейт-менеджер – Reduck, созданный на основе Redux.

Помимо фреймворка в экосистеме есть Modern.js Module и Modern.js Doc. Это инструменты для создания npm пакетов и фреймворк для создания документов соответственно.

https://modernjs.dev/en
👍4
Популярные ошибки при работе с React

В блоге Alex Khomenko вышел обзор популярных ошибок при работе с React. Некоторые из них:

Изменение стейта напрямую

При обновлении не примитивных значений, таких как объект или массив, нужно предоставить другую ссылку на объект. В ином случае, React не поймет, что стейт изменился и нужно сделать ре-рендер. Для решения проблемы можно использовать спред оператор, map для изменения объектов внутри массива, новое API structuredClone или библиотеки для работы с иммутабельностью, например Immer.js.

Забыть, что обновление стейта происходит асинхронно

Часто бывает, что пытаются обратиться к значению стейта сразу после его установки:

function Counter() {
const [count, setCount] = useState(0);

const increment = () => {
setCount(count + 1);
console.log(count); // 0
};
}


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

https://claritydev.net/blog/the-most-common-mistakes-when-using-react
👍6👎41
Сборник задач по TypeScript

Type Challenges – большой сборник задач по TypeScript, разбитый по категориям и уровню сложности. Решать задачи предлагается в песочнице TypeScript, там же уже есть тесты для проверки решения. Этот проект своего рода LeetCode, только для тренировки навыков написания TypeScript типов.

https://github.com/type-challenges/type-challenges

Еще один интересный проект по TypeScript – курс по типам от автора библиотеки TS-Pattern. Хоть курс и платный, первые четыре статьи бесплатные. В них много интересного материала по созданию типов в TypeScript, и в конце статьи есть задачи на практику.

https://type-level-typenoscript.com/
🔥153
Скажем нет “мерцающему” UI: useLayoutEffect и браузерная отрисовка

Надя Макаревич разобрала в своем блоге как работает хук useLayoutEffect и в чем его разница с useEffect, как работает браузерная отрисовка и как подружить useLayoutEffect и SSR.

Основное отличие в том, что useLayoutEffect запускается синхронно перед отрисовкой браузера. А useEffect будет запускаться асинхронно после отрисовки браузера, но это не гарантировано.

При SSR возможны “мерцания” интерфейса, если например где-то рендерятся компоненты по условию, вычисляемому в useLayoutEffect. Причина в том, что между моментом отрисовки браузером HTML/CSS полученного от сервера до выполнения useLayoutEffect проходит некоторое время. На сервере useLayoutEffect не выполняется, поэтому пользователю придется ждать когда он выполнится на клиенте после загрузки и исполнения JS. Чтобы избежать “мерцания” можно на сервере возвращать заглушку компонента.

https://www.developerway.com/posts/no-more-flickering-ui
2👎1
Гайд по необязательным параметрам в React

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

В блоге Джулс Блом вышел анимированный гайд по необязательным и альтернативным параметрам в React, которые редко используются. Например, useState принимает альтернативный аргумент – функцию инициализации. Этот подход можно использовать для ленивых вычислений, если нужно передать в стейт тяжелое вычисление или прочитать из localStorage. В таком случае, операция будет произведена один раз – при первом рендере компонента:

const getInitialState = () => Number(window.localStorage.getItem('count'))
const [count, setCount] = React.useState(getInitialState)


https://julesblom.com/writing/react-optional-parameters
👍7
Релиз TypeScript 5.1 Beta

Вышел релиз TypeScript 5.1 Beta, в котором есть несколько изменений для React.

Главное из них в том, что TypeScript позволит библиотекам объявлять свой собственный валидный JSX тип элемента. Связано это с тем, что в текущий момент функция компонента может вернуть только null | JSX.Element. Однако, в React компоненты могут возвращать ReactNode. Этот тип включает в себя number | string | Iterable<ReactNode> | undefined и также в будущем будет включать Promise<ReactNode>.

Таким образом, при обновлении на TypeScript 5.1+ открываются следующие возможности:
- использование асинхронных серверных компонентов.
- в функциях компонента возвращать значение из списка ReactNode без использования Fragment.
- другие возможности, такие как зеркальный курсор для редактирования JSX тега в VSCode и использование атрибутов JSX с пространством имен.

https://devblogs.microsoft.com/typenoscript/announcing-typenoscript-5-1-beta/
8
Новый хук useFormStatus

В React смержен ПР с добавлением нового экспериментального хука useFormStatus. Этот хук читает статус формы родительского компонента, если он существует:

const {pending, data, action, method} = useFormStatus();


Его можно использовать для отображения индикатора загрузки. Хук сделан на основе хука useTransition. Для реализации внутри используется startTransition и объект контекста.

https://github.com/facebook/react/pull/26722
👍9👎4🔥2
Анимация View Transitions в React

View Transitions API предлагает простой способ перехода визуального изменения DOM из одного состояния в другое.

На patterns.dev вышел обзор от Дом Кристи, в котором он объясняет, как использовать View Transition API в React и вместе с Next.js App Router.

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

https://www.patterns.dev/posts/view-transitions
👍5🔥1
Как структурировать Next.js приложения

В Next.js роутинг приложения и шаблоны основаны на расположении файлов. Этот подход влияет на то, как создаются приложения на Next.js.

Алекс Кондов в своем блоге описал, как структурировать Next.js приложения. За основу был взят принцип совместного размещения (co-location). В идеале вещи, которые связаны, должны находиться рядом. Поэтому можно создать папку модули, в которой будут находиться модули по доменам. Однако, по умолчанию в Next.js часть логики домена будет храниться в странице – в getServerSideProps и компоненте. Это приводит к фрагментации логики приложения.

Чтобы решить эту проблему, можно использовать гексагональную архитектуру и использовать роутинг Next.js как адаптер к нашей логики. Компонент страницы и логику getServerSideProps можно хранить в модулях и импортировать в страницу роута Next.js:


import ItemsPage from "modules/items/ItemsPage";
import itemsPageProvider from "modules/items/itemsPageProvider";

export default function Page({ user }) {
return <ItemsPage user={user} />
}

export async function getServerSideProps(context: NextPageContext) {
const { req, query } = context
const props = itemsPageProvider.getPageProps(req, query)
return { props }
}


https://alexkondov.com/structuring-next-applications/
👍3👎2
This media is not supported in your browser
VIEW IN TELEGRAM
Создание анимированного тултипа с помощью Framer Motion

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

https://sinja.io/blog/animated-tooltip-with-react-framer-motion
👍5
Что такое React Server Components

React Server Components стирает грань между сервером и клиентом, позволяя React компонентам загружать данные на сервере и отправлять отрендеренный компонент клиенту.

Преимущество React Server Components в уменьшении нагрузки браузера и уменьшении JS бандла клиента. Также уменьшается сетевой водопад браузера.

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

Также вышел релиз Next.js 13.4, в котором они объявили стабильным новый роутер с React Server Components. Это значит, что RSC можно использовать в продакшене.

https://www.viget.com/articles/what-even-are-react-server-components/
👍9👎1
Курс по React и TypeScript

Бесплатный курс от Matt Pocock по использованию TypeScript в React. Автор объясняет с самых азов, поэтому курс подойдет для начинающих.

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

https://www.totaltypenoscript.com/tutorials/react-with-typenoscript
👍6
React Canaries

Команда React представила новый канал релизов – Canary. Этот канал позволит фреймворкам типа Next.js адаптировать новые фичи раньше официального стабильного релиза.

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

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

https://react.dev/blog/2023/05/03/react-canaries
👍84👎1
Контекст, композиция и гибкость

Автор на примере поля ввода показывает, как улучшить гибкость компонента с помощью контекста. Если в компоненте есть разные варианты отображения, которые устанавливаются от страницы к странице, то можно использовать контекст для управления отображением компонента. Например:

function Input({ id, label, required = false }) {
const variant = useInputStyleContext()

const getHelperText = () => {
if (variant === 'showOptionals' && !required) return ' (optional)'
if (variant === 'showRequireds' && required) return '*'

return null
}

return (
<div>
<label htmlFor={id}>
{label}
{getHelperText()}
</label>
<input id={id} required={required} />
</div>
)
}


https://kyleshevlin.com/context-composition-and-flexibility
👍4
Изучаем React Server Component по викторинам

Дэн Абрамов провел опрос по React Server Component. В результате оказалось, что почти половина ответов неверны. RSC сложный, особенно это связано с тем, что по ней нет нормальной документации.

Херрингтон Даркхолм объяснил, как работает RSC изнутри, и по шагам показал, какие происходят действия при рендере компонента. Если кратко, то серверные компоненты передаются на клиент в виде JSON. Серверные компоненты, которые используют async/await вначале заменяются на клиенте на плейсхолдеры, а после выполнения передаются в браузер.

https://betterprogramming.pub/grok-react-server-component-by-quizzes-df4417905bc4
👍4🔥2
WunderGraph как альтернатива Vercel

Vercel предоставляет удобный сервис для деплоя ваших приложений. Но у него дорогие дополнительные услуги хранения: Vercel Postgres, Vercel KV и Vercel Blob.

В качестве альтернативы можно выбрать WunderGraph. Это open-source проект, который добавляет бэкенд для фронтенда. Можно выбрать свою базу данных или сервис с OpenApi, и WunderGraph CLI сгенерирует хуки для использования сервисов в приложении. Для деплоя на собственных серверах можно использовать Docker.

В качестве альтернативных сервисов Vercel Storage можно использовать Neon Tech для Postgres, Upstash как KV хранилище и Cloudflare R2 для хранения файлов.

https://javanoscript.plainenglish.io/dodging-the-vercel-storage-tax-there-are-better-open-source-alternatives-ef04e537b598
👍15
Понимание React Concurrency

В React 18 появились новые фичи Concurrency: useTransition и useDeferredValue. В своем блоге Слава Князев написал, как концептуально работают эти фичи изнутри.

Основная мысль React Concurrency в том, что при рендере следующего отображения текущее отображение остается отзывчивым. Под капотом для достижения такого эффекта используется requestIdleCallback() для вызова рендеринга приложения:

function renderConcurrent(Component) {
// Рендер отменяется, если стейт устарел
if (isCancelled) return;

for (let Child of Component) {
// Выполнить работу когда браузер освободится
requestIdleCallback(() => renderConcurrent(Child));
}
}


https://www.bbss.dev/posts/react-concurrency/
👍12
Legend-State 1.0

Вышел релиз библиотеки для управления состоянием Legend-State. Эта библиотека заявляет, что является одной из самых быстрых по бенчмаркам и может весить всего 3Кб.

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

В библиотеке есть встроенный плагин для сохранения стейта в localStorage или IndexedDB для создания персистентности.

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

Пример использования реактивных элементов:

function Component({ children }) {
// Этот компонент рендерится только один раз
const state = useObservable({ show: false, toggles: 0, text: 'Change me' })

useInterval(() => {
state.show.set(v => !v)
state.numToggles.set(v => v + 1)
}, 1000)

// Пропсы заканчивающиеся на $ биндят проп к Observable
// для конкретных изменений без ре-рендера
return (
<div>
<Legend.input value$={state.text} />
<Legend.div
className$={() => (
state.text.get().includes('Legend') && 'font-bold text-blue-500'
)}
>
{state.text}
</Legend.div>
<div>
Modal toggles: {state.numToggles}
</div>
<Show
if={state.show}
else={<div>Not showing modal</div>}
>
<div>Modal</div>
</Show>
</div>
)
}


https://legendapp.com/open-source/legend-state-v1/
👍11👎5
Как избежать моков в тестах с React Router

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

Для обертки компонента для тестов нужно использовать MemoryRouter. Вот пример такой функции, которая создана поверх render из RTL:

import React, { isValidElement } from "react";
import { render } from "@testing-library/react";
import { RouterProvider, createMemoryRouter } from "react-router-dom";

export function renderWithRouter(children, routes = []) {
const options = isValidElement(children)
? { element: children, path: "/" }
: children;

const router = createMemoryRouter([{ ...options }, ...routes], {
initialEntries: [options.path],
initialIndex: 1,
});

return render(<RouterProvider router={router} />);
}


Пример использования:

it("should ensure the page is rendered", () => {
renderWithRouter(<About />);

expect(screen.getByText("About page")).toBeInTheDocument();
});


https://webup.org/blog/how-to-avoid-mocking-in-react-router-v6-tests/
👍6