This media is not supported in your browser
VIEW IN TELEGRAM
Million Lint вышел в публичную бету
Million Lint – это VSCode расширение, которое выявляет медленный код и предлагает способы его оптимизации. Это как ESLint, но для производительности.
С помощью линтера можно узнать сколько времени в среднем занимает рендер компонента, как в React Devtools, но прямо в коде редактора. Также линтер дает возможность отследить размер бандла, сетевые запросы, пропсы, вызовы хуков.
Для того чтобы использовать линтер, надо установить VSCode расширение и добавить MillionCompiler, который выполняет динамический анализ отдельных компонентов. Пример настройки для Vite приложения:
https://million.dev/blog/lint
Million Lint – это VSCode расширение, которое выявляет медленный код и предлагает способы его оптимизации. Это как ESLint, но для производительности.
С помощью линтера можно узнать сколько времени в среднем занимает рендер компонента, как в React Devtools, но прямо в коде редактора. Также линтер дает возможность отследить размер бандла, сетевые запросы, пропсы, вызовы хуков.
Для того чтобы использовать линтер, надо установить VSCode расширение и добавить MillionCompiler, который выполняет динамический анализ отдельных компонентов. Пример настройки для Vite приложения:
import MillionCompiler from "@million/lint";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [MillionCompiler.vite(), react()],
});
https://million.dev/blog/lint
👍24🔥7
В React 19 добавят diff предупреждения гидратации
В React 19 планируют писать в консоле diff предупреждения гидратации. На данный момент, в React 18, если во время гидратации клиентская и серверная разметка отличаются, то React просто предупреждает о том, что есть несоответствие UI между клиентом и сервером.
В React 19 при определении diff будет учитываться работа хука useSyncExternalStore, который позволяет возвращать разный результат для клиента и сервера.
Вместо нескольких предупреждений в консоле будет одно общее, пример:
https://github.com/facebook/react/pull/28512
В React 19 планируют писать в консоле diff предупреждения гидратации. На данный момент, в React 18, если во время гидратации клиентская и серверная разметка отличаются, то React просто предупреждает о том, что есть несоответствие UI между клиентом и сервером.
В React 19 при определении diff будет учитываться работа хука useSyncExternalStore, который позволяет возвращать разный результат для клиента и сервера.
Вместо нескольких предупреждений в консоле будет одно общее, пример:
Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
--snip--
<Mismatch isClient={true}>
<div className="parent">
<main
+ className="child client"
- className="child server"
+ dir="ltr"
- dir="rtl"
>
https://github.com/facebook/react/pull/28512
GitHub
Add Diffs to Hydration Warnings by sebmarkbage · Pull Request #28512 · facebook/react
Stacked on #28502.
This builds on the mechanism in #28502 by adding a diff of everything we've collected so far to the thrown error or logged error.
This isn't actually a longest common sub...
This builds on the mechanism in #28502 by adding a diff of everything we've collected so far to the thrown error or logged error.
This isn't actually a longest common sub...
👍18❤5
Встроенная база данных Astro DB
В Astro появилась встроенная локальная база данных Astro DB. В ней можно хранить статьи, комментарии и пользователей, создавая разные таблицы.
БД сделана на основе libSQL. Изначально планировалось использовать SQLite, но из-за того, что SQLite нельзя использовать в браузере, выбрали libSQL. Его можно запустить в StackBlitz через WASM.
Astro DB устроена таким образом, что при каждом запуске дев-сервера БД (data.db) создается заново. При запуске команды astro dev происходит следующее:
- Создается пустая БД
- Создается схема БД из
- Заполняется БД из
Это дает вам простую, воспроизводимую БД, которая в дальнейшем служит единственным источником данных для приложения. Пример файла конфигурации БД
При работе с БД доступны основные операции записи, чтения, фильтрации, сравнения и объединения. Пример запроса:
https://astro.build/blog/astro-db-deep-dive/
В Astro появилась встроенная локальная база данных Astro DB. В ней можно хранить статьи, комментарии и пользователей, создавая разные таблицы.
БД сделана на основе libSQL. Изначально планировалось использовать SQLite, но из-за того, что SQLite нельзя использовать в браузере, выбрали libSQL. Его можно запустить в StackBlitz через WASM.
Astro DB устроена таким образом, что при каждом запуске дев-сервера БД (data.db) создается заново. При запуске команды astro dev происходит следующее:
- Создается пустая БД
.astro/data.db- Создается схема БД из
db/config.ts- Заполняется БД из
db/seed.tsЭто дает вам простую, воспроизводимую БД, которая в дальнейшем служит единственным источником данных для приложения. Пример файла конфигурации БД
db/config.ts:
import { defineDb, defineTable, column } from 'astro:db';
const Comment = defineTable({
columns: {
author: column.text(),
body: column.text(),
}
})
export default defineDb({
tables: { Comment },
})
При работе с БД доступны основные операции записи, чтения, фильтрации, сравнения и объединения. Пример запроса:
import { db, eq, Comment, Author } from 'astro:db';
const comments = await db.select()
.from(Comment)
.innerJoin(Author, eq(Comment.authorId, Author.id));
https://astro.build/blog/astro-db-deep-dive/
Astro
Astro DB: A Deep Dive | Astro
Yesterday we launched a fully managed SQL database service designed exclusively for the Astro web framework. Let's dive into the implementation details of Astro DB: how it works, why we built it, and why we're adopting libSQL.
👍13👎1🔥1
React Data Grid: быстрый датагрид как Excel
react-data-grid – компонент для отображения данных в датагриде. Компонент поддерживает виртуализацию, tree-shaking, используется только одна внешняя зависимость, а gzip размер пакета 14.5 КБ.
Компонент поддерживает фильтрацию, группировку, выбор строк, форматирование ячеек, бесконечную подгрузку и многое другое при работе с данными. На сайте есть много готовых примеров, среди которых пример рендера миллиона ячеек.
https://adazzle.github.io/react-data-grid/
react-data-grid – компонент для отображения данных в датагриде. Компонент поддерживает виртуализацию, tree-shaking, используется только одна внешняя зависимость, а gzip размер пакета 14.5 КБ.
Компонент поддерживает фильтрацию, группировку, выбор строк, форматирование ячеек, бесконечную подгрузку и многое другое при работе с данными. На сайте есть много готовых примеров, среди которых пример рендера миллиона ячеек.
import 'react-data-grid/lib/styles.css';
import DataGrid from 'react-data-grid';
const columns = [
{ key: 'id', name: 'ID' },
{ key: 'noscript', name: 'Title' }
];
const rows = [
{ id: 0, noscript: 'Example' },
{ id: 1, noscript: 'Demo' }
];
function App() {
return <DataGrid columns={columns} rows={rows} />;
}
https://adazzle.github.io/react-data-grid/
👍28
В React нельзя сделать типобезопасным children
При работе с TypeScript и JSX иногда появляются ситуации, когда надо ограничить дочерние элементы определенным типом. Например, есть компонент Select, который принимает компоненты Option как дочерние. Вы хотите убедиться, что компоненты Option единственные дочерние компоненты, которые передаются в Select:
Ограничить тип компонента, передаваемого как дочерний, через TypeScript невозможно. Любой <Component /> или HTML тег это JSX.Element.
В качестве заключения стоит отметить, что главная магия React в композиции. Можно компоновать компоненты любым удобным способом. Ограничивая children у компонента, вы нарушаете эту возможность компоновки.
Возможно вместо потенциального ограничения типа children можно использовать другой проп:
https://www.totaltypenoscript.com/type-safe-children-in-react-and-typenoscript
При работе с TypeScript и JSX иногда появляются ситуации, когда надо ограничить дочерние элементы определенным типом. Например, есть компонент Select, который принимает компоненты Option как дочерние. Вы хотите убедиться, что компоненты Option единственные дочерние компоненты, которые передаются в Select:
// Хорошо
<Select>
<Option value="1">One</Option>
<Option value="2">Two</Option>
<Option value="3">Three</Option>
</Select>
// Плохо
<Select>
<div>One</div>
<div>Two</div>
<div>Three</div>
</Select>
Ограничить тип компонента, передаваемого как дочерний, через TypeScript невозможно. Любой <Component /> или HTML тег это JSX.Element.
В качестве заключения стоит отметить, что главная магия React в композиции. Можно компоновать компоненты любым удобным способом. Ограничивая children у компонента, вы нарушаете эту возможность компоновки.
Возможно вместо потенциального ограничения типа children можно использовать другой проп:
<Select
options={[
{ value: "1", label: "One" },
{ value: "2", label: "Two" },
{ value: "3", label: "Three" },
]}
/>
https://www.totaltypenoscript.com/type-safe-children-in-react-and-typenoscript
Total TypeScript
You Can't Make Children "Type Safe" in React & TypeScript
Learn why it's not possible to restrict the type of children in React components with TypeScript.
👍22👎1
Оптимизация JavaScript для развлечения и ради прибыли
Обзор распространенных методов оптимизации JavaScript, которые могут быть полезны и для React приложений. Для каждого метода автор приводит бенчмарк для сравнения скорости выполнения. Кратко, о чем пишет автор:
- Избегайте работы. Если начинаете делать оптимизацию, то сначала уменьшите количество выполняемых операций: мемоизация и ленивые вычисления.
- Избегайте сравнение строк. Для сравнения строк используется функция strcmp(a, b), сложность которой O(n). Если создаете enum, то лучше не использовать строки:
- Избегайте разных форм. Используйте одинаковую форму объекта в одном массиве.
- Избегайте методов объекта/массива. Писать в функциональном стиле через цепочку методов дороже чем в императивном. Пример:
Для каждой операции будет создаваться копия массива. Цикл запускается 3 раза для разных операций, когда через цикл for запустится один раз.
- Используйте правильные структуры данных. Неправильно подобранная структура данных может ухудшить оптимизацию. Надо знать про существующие Map и Set, а также узнать о связанных списках, приоритетной очереди и деревьях. Например, использование Set.has гораздо быстрее чем Array.includes.
Обратите внимание, что компромиссом в производительности часто является читабельность, поэтому вопрос о том, когда лучше выбирать производительность, а не читабельность, остается на усмотрение разработчика.
https://romgrk.com/posts/optimizing-javanoscript
Обзор распространенных методов оптимизации JavaScript, которые могут быть полезны и для React приложений. Для каждого метода автор приводит бенчмарк для сравнения скорости выполнения. Кратко, о чем пишет автор:
- Избегайте работы. Если начинаете делать оптимизацию, то сначала уменьшите количество выполняемых операций: мемоизация и ленивые вычисления.
- Избегайте сравнение строк. Для сравнения строк используется функция strcmp(a, b), сложность которой O(n). Если создаете enum, то лучше не использовать строки:
// Плохо
enum Position {
TOP = 'TOP',
BOTTOM = 'BOTTOM',
}
// Лучше
enum Position {
TOP, // = 0
BOTTOM, // = 1
}
- Избегайте разных форм. Используйте одинаковую форму объекта в одном массиве.
- Избегайте методов объекта/массива. Писать в функциональном стиле через цепочку методов дороже чем в императивном. Пример:
const result =
[1.5, 3.5, 5.0]
.map(n => Math.round(n))
.filter(n => n % 2 === 0)
.reduce((a, n) => a + n, 0)
Для каждой операции будет создаваться копия массива. Цикл запускается 3 раза для разных операций, когда через цикл for запустится один раз.
- Используйте правильные структуры данных. Неправильно подобранная структура данных может ухудшить оптимизацию. Надо знать про существующие Map и Set, а также узнать о связанных списках, приоритетной очереди и деревьях. Например, использование Set.has гораздо быстрее чем Array.includes.
Обратите внимание, что компромиссом в производительности часто является читабельность, поэтому вопрос о том, когда лучше выбирать производительность, а не читабельность, остается на усмотрение разработчика.
https://romgrk.com/posts/optimizing-javanoscript
👎18👍11🔥3
Новый хук useActionState
В React появится новый хук useActionState, который заменит существующий ReactDOM хук useFormState. Этот хук призван исправить некоторую путаницу и ограничения хука useFormState.
Хук useFormState импортировался из ReactDOM и был предназначен только для отслеживания стейта action у формы. Однако не обязательно было использовать его для формы, его можно было использовать для отслеживания любой асинхронной функции.
Команда React переименовала хук useFormState в useActionState, добавила стейт pending в возвращаемый результат и переместила хук в пакет
Пример использования хука:
Под капотом вызов action вызывает под собой startTransition, код обработчика можно заменить на такой:
https://github.com/facebook/react/pull/28491
В React появится новый хук useActionState, который заменит существующий ReactDOM хук useFormState. Этот хук призван исправить некоторую путаницу и ограничения хука useFormState.
Хук useFormState импортировался из ReactDOM и был предназначен только для отслеживания стейта action у формы. Однако не обязательно было использовать его для формы, его можно было использовать для отслеживания любой асинхронной функции.
Команда React переименовала хук useFormState в useActionState, добавила стейт pending в возвращаемый результат и переместила хук в пакет
react. Пример использования хука:
import { useActionState, useRef } from "react";
function Form({ someAction }) {
const ref = useRef(null);
const [state, action, isPending] = useActionState(someAction);
async function handleSubmit() {
await action({ email: ref.current.value });
}
return (
<div>
<input ref={ref} type="email" name="email" disabled={isPending} />
<button onClick={handleSubmit} disabled={isPending}>
Submit
</button>
{state.errorMessage && <p>{state.errorMessage}</p>}
</div>
);
}
Под капотом вызов action вызывает под собой startTransition, код обработчика можно заменить на такой:
const [state, setState] = useState(null);
const [isPending, setIsPending] = useTransition();
function handleSubmit() {
startTransition(async () => {
const response = await someAction({ email: ref.current.value });
setState(response);
});
}
https://github.com/facebook/react/pull/28491
GitHub
Add `React.useActionState` by rickhanlonii · Pull Request #28491 · facebook/react
Overview
Depends on #28514
This PR adds a new React hook called useActionState to replace and improve the ReactDOM useFormState hook.
Motivation
This hook intends to fix some of the confusion and l...
Depends on #28514
This PR adds a new React hook called useActionState to replace and improve the ReactDOM useFormState hook.
Motivation
This hook intends to fix some of the confusion and l...
👍9🔥3
Нестабильные тесты в React: обнаружение, предотвращение и инструменты
Нестабильные тесты в React – это тесты, которые в большинстве случаев проходят успешно, но иногда ломаются, и все это без изменений в коде или тесте — просто без причины.
Вот некоторые причины, по которым тесты могут быть нестабильными:
- Внешние зависимости. Например, компонент получает данные по API и отображает ответ. Если в API вернет не то, что ожидал компонент, то тест сломается.
- Проблемы со временем. Например, если тесту надо проверять UI элемент до или после анимации или CSS перехода, то такой тест нестабильный.
- Асинхронные операции. К этим операциям относится пользовательский ввод, обновление UI, получение данных из API. Если тест не ждет завершения операции перед утверждением, то такой тест может сломаться.
- Дырявый стейт. Тест может сломаться, если внутри него происходит мутация глобального состояния или неучтенные сайд эффекты. Эти изменения могут повлиять на запуск следующего теста.
Для предотвращения нестабильных тестов, используйте следующие правила:
- Хорошо структурируйте тесты. Они должны быть независимыми, иметь осмысленное название, должны создавать собственный экземпляр компонента при запуске.
- Используйте моки. Заменяйте внешние зависимости на контролируемые моки. Это придает тесту более предсказуемое поведение.
- Используйте beforeEach и afterEach, чтобы каждый тест начинался с чистого листа.
- Если есть возможность проводить тестирование при помощи снимков, то используйте это.
https://semaphoreci.com/blog/flaky-react
Нестабильные тесты в React – это тесты, которые в большинстве случаев проходят успешно, но иногда ломаются, и все это без изменений в коде или тесте — просто без причины.
Вот некоторые причины, по которым тесты могут быть нестабильными:
- Внешние зависимости. Например, компонент получает данные по API и отображает ответ. Если в API вернет не то, что ожидал компонент, то тест сломается.
- Проблемы со временем. Например, если тесту надо проверять UI элемент до или после анимации или CSS перехода, то такой тест нестабильный.
- Асинхронные операции. К этим операциям относится пользовательский ввод, обновление UI, получение данных из API. Если тест не ждет завершения операции перед утверждением, то такой тест может сломаться.
- Дырявый стейт. Тест может сломаться, если внутри него происходит мутация глобального состояния или неучтенные сайд эффекты. Эти изменения могут повлиять на запуск следующего теста.
Для предотвращения нестабильных тестов, используйте следующие правила:
- Хорошо структурируйте тесты. Они должны быть независимыми, иметь осмысленное название, должны создавать собственный экземпляр компонента при запуске.
- Используйте моки. Заменяйте внешние зависимости на контролируемые моки. Это придает тесту более предсказуемое поведение.
- Используйте beforeEach и afterEach, чтобы каждый тест начинался с чистого листа.
- Если есть возможность проводить тестирование при помощи снимков, то используйте это.
test("Modal opens", async () => {
render(<Modal />);
fireEvent.click(screen.getByText("Open Modal"));
await waitFor(() => expect(screen.getByTestId("modal")).toBeInTheDocument());
});
https://semaphoreci.com/blog/flaky-react
Semaphore
Flaky Tests In React: Detection, Prevention and Tools - Semaphore
This guide explores the causes, detection, and fixes for flaky tests in React, ensuring code quality and a smooth user experience.
👍10
Более быстрый React.memo()
В React.memo используется общая функция shallowCompare, которую можно значительно оптимизировать с помощью специальной функции сравнения, которая предполагает, что объекты для сравнения – это React пропсы.
Автор блога romgrk сделал две версии функции проверки пропсов для React.memo(): безопасную и небезопасную. Небезопасная быстрее общей shallowCompare в два раза, вот ее код:
При сравнении объектов { a: 1, b: undefined }, { a: 1, c: undefined } эта функция вернет true, т.е. скажет что они равны. Для общего сравнения двух объектов данная функция не подходит, но для сравнения пропсов React такая функция будет работать.
https://romgrk.com/posts/react-fast-memo/
В React.memo используется общая функция shallowCompare, которую можно значительно оптимизировать с помощью специальной функции сравнения, которая предполагает, что объекты для сравнения – это React пропсы.
Автор блога romgrk сделал две версии функции проверки пропсов для React.memo(): безопасную и небезопасную. Небезопасная быстрее общей shallowCompare в два раза, вот ее код:
function fastCompareUnsafe(a, b) {
var aLength = 0;
var bLength = 0;
for (var key in a) {
aLength += 1;
if (!Object.is(a[key], b[key])) {
return false;
}
}
for (var _ in b) {
bLength += 1;
}
return aLength === bLength;
}
При сравнении объектов { a: 1, b: undefined }, { a: 1, c: undefined } эта функция вернет true, т.е. скажет что они равны. Для общего сравнения двух объектов данная функция не подходит, но для сравнения пропсов React такая функция будет работать.
https://romgrk.com/posts/react-fast-memo/
Romgrk
A faster React.memo()
romgrk's personal blog
👍11
Создаем прогресс загрузки страницы с помощью Transition API
В Build UI рассказали, как можно создать прогресс загрузки страницы с помощью Transition API. В результате получился хук useProgress и компоненты прогресса с Framer Motion:
Чтобы показать прогресс загрузки страницы в Next.js, то необходимо использовать Transition API. Пример:
В Next.js router.push не возвращает промис, но использует Transition API. React вызывает все обновления стейта внутри startTransition одновременно в фоне. Если какое-то обновление будет приостановлено, в нашем случае так делает router.push, то React отложит отрисовку UI, пока все обновления не будут завершены.
Можно считать, что вызов startTransition создает форк текущего дерева и применяет все обновления на этом дереве. После того, как все обновления завершены, новое дерево отрисовывается в DOM.
https://buildui.com/posts/global-progress-in-nextjs
В Build UI рассказали, как можно создать прогресс загрузки страницы с помощью Transition API. В результате получился хук useProgress и компоненты прогресса с Framer Motion:
const { start, done, reset, state, value } = useProgress();
Чтобы показать прогресс загрузки страницы в Next.js, то необходимо использовать Transition API. Пример:
const router = useRouter();
const { start, done } = useProgress();
return (
<Link
onClick={(e) => {
e.preventDefault();
start();
startTransition(() => {
router.push(href);
done();
});
}}
href={href}
>
{children}
</Link>
);
В Next.js router.push не возвращает промис, но использует Transition API. React вызывает все обновления стейта внутри startTransition одновременно в фоне. Если какое-то обновление будет приостановлено, в нашем случае так делает router.push, то React отложит отрисовку UI, пока все обновления не будут завершены.
Можно считать, что вызов startTransition создает форк текущего дерева и применяет все обновления на этом дереве. После того, как все обновления завершены, новое дерево отрисовывается в DOM.
https://buildui.com/posts/global-progress-in-nextjs
Build UI
Global progress in Next.js
👍13❤2
Предоставляйте API платформы вместо обертки
В блоге Джима Нильсена вышла критика в сторону Metadata API в Next.js.
Автор пишет, что предпочитает писать код как можно ближе к тому, как он будет запускаться, а это означает, что нужно оставаться как можно ближе к API платформы. API платформы стандартизировано и при его использовании позволяет легко переключаться с одного фреймворка на другой. Помимо написания кода, ты его запускаешь и дебажишь. Проще дебажить код, который выглядит 1:1 как ты написал.
Metadata API в Next.js – это обертка для генерации meta-тегов. Под капотом у нее много логики, поэтому ее не так просто дебажить:
Как видно из примера, Metadata API не только генерирует мета-теги, но и генерирует link-теги.
Еще одно противоречие в том, что для генерации мета-тега viewport используется свое API:
https://blog.jim-nielsen.com/2024/expose-platform-apis-over-wrapping-them/
В блоге Джима Нильсена вышла критика в сторону Metadata API в Next.js.
Автор пишет, что предпочитает писать код как можно ближе к тому, как он будет запускаться, а это означает, что нужно оставаться как можно ближе к API платформы. API платформы стандартизировано и при его использовании позволяет легко переключаться с одного фреймворка на другой. Помимо написания кода, ты его запускаешь и дебажишь. Проще дебажить код, который выглядит 1:1 как ты написал.
Metadata API в Next.js – это обертка для генерации meta-тегов. Под капотом у нее много логики, поэтому ее не так просто дебажить:
export const metadata = {
authors: [
{ name: 'Seb' },
{ name: 'Josh', url: 'https://nextjs.org' }
],
}
// Результат:
<meta name="author" content="Seb" />
<meta name="author" content="Josh" />
<link rel="author" href="https://nextjs.org" />
Как видно из примера, Metadata API не только генерирует мета-теги, но и генерирует link-теги.
Еще одно противоречие в том, что для генерации мета-тега viewport используется свое API:
export const viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 1,
userScalable: false,
}
// Результат
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
/>
https://blog.jim-nielsen.com/2024/expose-platform-apis-over-wrapping-them/
nextjs.org
Next.js by Vercel - The React Framework
Production grade React applications that scale. The world's leading companies use Next.js by Vercel to build static and dynamic websites and web applications.
👍9❤1
В Waku появился pages router
В Waku появился файловый роутер страниц. Можно создавать новые страницы просто создав файл в ./src/pages. Поддерживаются сегментированные параметры blog/[slug].tsx и шаблоны _layout.tsx для оборачивания дочерних роутов.
Новая страница создается через создание файла с двумя экспортами: дефолтный для компонента и именованный getConfig для функции, которая возвращает объект настройки. В getConfig можно определить метод рендера страницы: динамический SSR и статический SSG.
Если роут с сегментированными параметрами и с статическим методом рендера, то надо заранее определить staticPaths:
Статический рендер происходит во время сборки и его можно использовать в шаблонных роутах_layout.tsx, а дочерние роуты рендерить динамически.
https://waku.gg/blog/introducing-pages-router
В Waku появился файловый роутер страниц. Можно создавать новые страницы просто создав файл в ./src/pages. Поддерживаются сегментированные параметры blog/[slug].tsx и шаблоны _layout.tsx для оборачивания дочерних роутов.
Новая страница создается через создание файла с двумя экспортами: дефолтный для компонента и именованный getConfig для функции, которая возвращает объект настройки. В getConfig можно определить метод рендера страницы: динамический SSR и статический SSG.
Если роут с сегментированными параметрами и с статическим методом рендера, то надо заранее определить staticPaths:
// ./src/pages/blog/[slug].tsx
export default async function BlogArticlePage({ slug }) {
// --snip--
}
const getData = async (slug) => {
// --snip--
};
export const getConfig = async () => {
return {
render: 'static',
staticPaths: ['introducing-waku', 'introducing-pages-router'],
};
};
Статический рендер происходит во время сборки и его можно использовать в шаблонных роутах_layout.tsx, а дочерние роуты рендерить динамически.
https://waku.gg/blog/introducing-pages-router
Waku
Introducing “pages router” — Waku
Bringing a minimal API to the modern React server components era.
👍3👎1
onCaughtError и onUncaughtError для обработки ошибок
В React 19 добавятся новые способы обработки ошибок. Это касается новых колбеков для React Root (createRoot и hydrateRoot):
- onCaughtError сообщает об ошибках, обнаруженных Error Boundary
- onUncaughtError сообщает о необнаруженных ошибках
- onRecoverableError теперь использует ES Error Cause, чтобы сообщить об исходной причине ошибок
В документацию добавили много примеров с использованием новых колбеков, например, колбек для необработанных ошибок:
Если использовать onRecoverableError в hydrateRoot, то можно узнать причину ошибки гидратации, пример такого сообщения:
https://github.com/reactjs/react.dev/pull/6742
В React 19 добавятся новые способы обработки ошибок. Это касается новых колбеков для React Root (createRoot и hydrateRoot):
- onCaughtError сообщает об ошибках, обнаруженных Error Boundary
- onUncaughtError сообщает о необнаруженных ошибках
- onRecoverableError теперь использует ES Error Cause, чтобы сообщить об исходной причине ошибок
В документацию добавили много примеров с использованием новых колбеков, например, колбек для необработанных ошибок:
const root = createRoot(container, {
onUncaughtError: (error, errorInfo) => {
if (error.message !== 'Known error') {
reportUncaughtError({
error,
componentStack: errorInfo.componentStack
});
}
}
});
root.render(<App />);
Если использовать onRecoverableError в hydrateRoot, то можно узнать причину ошибки гидратации, пример такого сообщения:
Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
…
<App>
<span>
+ Client
- Server
https://github.com/reactjs/react.dev/pull/6742
GitHub
Add docs for onCaughtError and onUncaughtError by rickhanlonii · Pull Request #6742 · reactjs/react.dev
Overview
In the React 19 canary we've added two new root options:
onCaughtError
onUncaughtError
This PR adds docs for how to use these new options to display an error dialog.
Additionally, th...
In the React 19 canary we've added two new root options:
onCaughtError
onUncaughtError
This PR adds docs for how to use these new options to display an error dialog.
Additionally, th...
👍11🔥3👎1
Использование Suspense с React Query
Хук useQuery в React Query возвращает опциональный объект data, он может быть или не быть, зависит от результата загрузки. Если использовать хук useSuspenseQuery, который работает с Suspense, то data будет всегда. Пример использования:
В примере Suspense ответственен за отображение индикатора загрузки. Если запрос закончится неуспешно, то ErrorBoundary отобразит ошибку.
В этом подходе есть ограничения. Например, если изменится queryKey, то Suspense заново отобразит индикатор загрузки. Если надо чтобы компонент Example продолжал отображаться во время повторной загрузки, то придется дополнительно использовать useTransition или useDeferredValue.
Пример с useTransition:
https://www.teemutaskula.com/blog/exploring-query-suspense
Хук useQuery в React Query возвращает опциональный объект data, он может быть или не быть, зависит от результата загрузки. Если использовать хук useSuspenseQuery, который работает с Suspense, то data будет всегда. Пример использования:
function Example() {
const { data } = useSuspenseQuery({
queryKey: ["todos"],
queryFn: fetchTodos,
});
return (
<ul>
{data.map((todo) => (
<li key={todo.id}>{todo.noscript}</li>
))}
</ul>
);
}
import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
function Parent() {
return (
<Suspense fallback={<div>Loading...</div>}>
<ErrorBoundary fallback={<div>Something went wrong!</div>}>
<Example />
</ErrorBoundary>
</Suspense>
);
}
В примере Suspense ответственен за отображение индикатора загрузки. Если запрос закончится неуспешно, то ErrorBoundary отобразит ошибку.
В этом подходе есть ограничения. Например, если изменится queryKey, то Suspense заново отобразит индикатор загрузки. Если надо чтобы компонент Example продолжал отображаться во время повторной загрузки, то придется дополнительно использовать useTransition или useDeferredValue.
Пример с useTransition:
import { useTransition, useState } from "react";
import { useSuspenseQuery } from "@tanstack/react-query";
function Example() {
const [page, setPage] = useState(1);
const [isPending, startTransition] = useTransition();
const { data } = useSuspenseQuery({
queryKey: ["todos", page],
queryFn: () => fetchTodos({ page }),
});
function handleNextPage() {
startTransition(() => {
setPage((prev) => prev + 1);
});
}
return (
<div>
{isPending && <p>Fetching...</p>}
<ul>{/* ...render todos... */}</ul>
<button onClick={handleNextPage}>Next</button>
</div>
);
}
https://www.teemutaskula.com/blog/exploring-query-suspense
Teemutaskula
Exploring using Suspense with React Query
Learn how I explored using Suspense with React Query and what issues I encountered and how I solved them.
👍8👎4
CSS в React Server Components
В блоге Джошуа Комо вышла статья про использование CSS-in-JS библиотек в React Server Components. Если вы используете CSS-in-JS библиотеки, то эта статья даст вам полезную информацию о том, какие есть варианты использования в RSC. Если не используете CSS-in-JS библиотеки, то сможете лучше разобраться как устроен RSC.
В отличие от клиентских компонентов, RSC выполняется только на сервере. RSC это как PHP шаблоны, которые рендерятся на сервере и возвращают HTML. RSC не поддерживают хуки, из-за того что они вызывают ре-рендеры, а RSC рендерится только один раз на сервере.
CSS-in-JS библиотеки, которые выполняются в runtime, используют контекст и динамически генерируют стили компонентов, помещая их в тег <style>. Эти стили могут обновляться, если обновляется компонент. В парадигме RSC такой подход не будет работать.
Для того чтобы использовать CSS-in-JS библиотеки в RSC, стоит использовать zero-runtime CSS-in-JS библиотеки, которые генерируют стили во время сборки приложения. Пример таких библиотек: Panda CSS и Pigment CSS. Пример Panda CSS:
Компилируется в:
https://www.joshwcomeau.com/react/css-in-rsc/
В блоге Джошуа Комо вышла статья про использование CSS-in-JS библиотек в React Server Components. Если вы используете CSS-in-JS библиотеки, то эта статья даст вам полезную информацию о том, какие есть варианты использования в RSC. Если не используете CSS-in-JS библиотеки, то сможете лучше разобраться как устроен RSC.
В отличие от клиентских компонентов, RSC выполняется только на сервере. RSC это как PHP шаблоны, которые рендерятся на сервере и возвращают HTML. RSC не поддерживают хуки, из-за того что они вызывают ре-рендеры, а RSC рендерится только один раз на сервере.
CSS-in-JS библиотеки, которые выполняются в runtime, используют контекст и динамически генерируют стили компонентов, помещая их в тег <style>. Эти стили могут обновляться, если обновляется компонент. В парадигме RSC такой подход не будет работать.
Для того чтобы использовать CSS-in-JS библиотеки в RSC, стоит использовать zero-runtime CSS-in-JS библиотеки, которые генерируют стили во время сборки приложения. Пример таких библиотек: Panda CSS и Pigment CSS. Пример Panda CSS:
import { styled } from '../styled-system/jsx'
export default function Homepage() {
return (
<BigRedButton>
Click me!
</BigRedButton>
);
}
const BigRedButton = styled.button`
font-size: 2rem;
color: red;
`;
Компилируется в:
/* /styles.css */
.font-size_2rem {
font-size: 2rem;
}
.color_red {
color: red;
}
/* /components/Home.js */
export default function Homepage() {
return (
<button className="font-size_2rem color_red">
Click me!
</button>
);
}
https://www.joshwcomeau.com/react/css-in-rsc/
Joshwcomeau
CSS in React Server Components • Josh W. Comeau
You can’t make an omelette without cracking a few eggs, and when the core React team unveiled their vision for the future of React, some of my favourite libraries got scrambled 😅. In this blog post, we’re going to explore the compatibility issues between…
👍5❤1
Next.js 14.2
Вышел релиз Next.js 14.2. Что нового:
- Улучшение производительности локальной разработки с Turbopack. Интегрировали Lightning CSS – быстрый CSS бандл и минификатор, написанный на Rust. Пока еще Turbopack нельзя использовать для прод сборки.
- Оптимизация сборки:
* Улучшен Tree-shaking. Например, импорт одной иконки из файла с "use client" больше не включает в бандл все остальные иконки.
* Уменьшено потребление памяти для прод сборки.
* Улучшена сборка CSS для предотвращения конфликтов стилей.
- Появилась возможность изменить время жизни кэша страницы.
- Улучшен DX ошибок. Например, при ошибке гидратации сообщается, где и как отличается разметка.
- Поддержка новых фич из React 19, например Server Action и связанные с ним хуки.
https://nextjs.org/blog/next-14-2
Вышел релиз Next.js 14.2. Что нового:
- Улучшение производительности локальной разработки с Turbopack. Интегрировали Lightning CSS – быстрый CSS бандл и минификатор, написанный на Rust. Пока еще Turbopack нельзя использовать для прод сборки.
- Оптимизация сборки:
* Улучшен Tree-shaking. Например, импорт одной иконки из файла с "use client" больше не включает в бандл все остальные иконки.
* Уменьшено потребление памяти для прод сборки.
* Улучшена сборка CSS для предотвращения конфликтов стилей.
- Появилась возможность изменить время жизни кэша страницы.
- Улучшен DX ошибок. Например, при ошибке гидратации сообщается, где и как отличается разметка.
- Поддержка новых фич из React 19, например Server Action и связанные с ним хуки.
https://nextjs.org/blog/next-14-2
nextjs.org
Next.js 14.2
Next.js 14.2 includes development, production, and caching improvements. Including new configuration options, 99% Turbopack tests passing, and more.
👍15❤1
React 18.3
Команда React планирует минорный релиз React 18.3. В нем не будет никаких изменений в API, только появятся новые предупреждения об устаревших функциях, чтобы помочь перейти на React 19. Например, React будет предупреждать, если будут использоваться строковые рефы, defaultProps вместе с FC/memo компонентами и при спреде пропа key в элемент.
https://github.com/facebook/react/pull/28843
Команда React планирует минорный релиз React 18.3. В нем не будет никаких изменений в API, только появятся новые предупреждения об устаревших функциях, чтобы помочь перейти на React 19. Например, React будет предупреждать, если будут использоваться строковые рефы, defaultProps вместе с FC/memo компонентами и при спреде пропа key в элемент.
https://github.com/facebook/react/pull/28843
GitHub
Bump version from 18.2 to 18.3 by acdlite · Pull Request #28843 · facebook/react
Not for merge
We're going to use this branch to release a minor 18.3 release based off the published 18.2 release revision. This will include some additional warnings to assist in upgrading to ...
We're going to use this branch to release a minor 18.3 release based off the published 18.2 release revision. This will include some additional warnings to assist in upgrading to ...
👍17
Как изменится API в React 19
Эндрю Кларк, один из разработчиков React, поделился как изменится API в React 19. О чем он пишет:
- Хуки useMemo, useCallback и мемоизация будут обрабатываться с помощью автоматической оптимизации React Compiler.
- forwardRef станет устаревшим, поскольку ref станет пропом.
- Вместо React.lazy импорта будут использоваться серверные компоненты.
- Хук useContext будет заменен на хук use, который более гибкий и позволяет читать значения Promise и Context.
- Context.Provider будет заменен более простой записью Context.
https://thoughtbot.com/blog/get-your-codebase-ready-for-react-19
Эндрю Кларк, один из разработчиков React, поделился как изменится API в React 19. О чем он пишет:
- Хуки useMemo, useCallback и мемоизация будут обрабатываться с помощью автоматической оптимизации React Compiler.
- forwardRef станет устаревшим, поскольку ref станет пропом.
- Вместо React.lazy импорта будут использоваться серверные компоненты.
- Хук useContext будет заменен на хук use, который более гибкий и позволяет читать значения Promise и Context.
- Context.Provider будет заменен более простой записью Context.
https://thoughtbot.com/blog/get-your-codebase-ready-for-react-19
thoughtbot
Get your codebase ready for React 19
Is your app ready for what’s coming up in React 19’s release? Get prepared with what is going to change ahead of its release date.
👍34
HTML атрибуты и DOM свойства
В своем блоге Джейк Арчибальд написал статью о DOM свойствах и HTML атрибутах, в том числе как React с ними работает. Также написал, как React 19 улучшит поддержку веб-компонентов.
Атрибуты и свойства элемента фундаментально разные вещи. Можно иметь атрибут и свойство одинакового имени и разного значения. Например:
Чаще всего это различие не важно. Но если надо углубиться в HTML, то хорошо знать в чем разница. Основные различия:
- Атрибуты сериализуются в HTML через свойство outerHTML.
- Атрибуты всегда типа string, когда свойства могут быть любыми типами.
- Атрибуты не чувствительны к регистру, тогда как свойства чувствительны к регистру.
Также стоит знать про рефлексию атрибутов и свойств. Например, в поле ввода input есть свойство input и атрибут input. Свойство инпут не рефлексирует атрибуту value. Вместо этого свойство defaultValue рефлексирует атрибуту value. Пример:
По мнению автора, это правильная логика. Атрибуты предназначены для настройки, тогда как свойства уже могут иметь состояние.
https://jakearchibald.com/2024/attributes-vs-properties/
В своем блоге Джейк Арчибальд написал статью о DOM свойствах и HTML атрибутах, в том числе как React с ними работает. Также написал, как React 19 улучшит поддержку веб-компонентов.
Атрибуты и свойства элемента фундаментально разные вещи. Можно иметь атрибут и свойство одинакового имени и разного значения. Например:
<div foo="bar">…</div>
<noscript>
const div = document.querySelector('div[foo=bar]');
console.log(div.getAttribute('foo')); // 'bar'
console.log(div.foo); // undefined
div.foo = 'hello world';
console.log(div.getAttribute('foo')); // 'bar'
console.log(div.foo); // 'hello world'
</noscript>
Чаще всего это различие не важно. Но если надо углубиться в HTML, то хорошо знать в чем разница. Основные различия:
- Атрибуты сериализуются в HTML через свойство outerHTML.
- Атрибуты всегда типа string, когда свойства могут быть любыми типами.
- Атрибуты не чувствительны к регистру, тогда как свойства чувствительны к регистру.
Также стоит знать про рефлексию атрибутов и свойств. Например, в поле ввода input есть свойство input и атрибут input. Свойство инпут не рефлексирует атрибуту value. Вместо этого свойство defaultValue рефлексирует атрибуту value. Пример:
<input type="text" value="default" />
<noscript>
const input = document.querySelector('input');
console.log(input.getAttribute('value')); // 'default'
console.log(input.value); // 'default'
console.log(input.defaultValue); // 'default'
input.defaultValue = 'new default';
console.log(input.getAttribute('value')); // 'new default'
console.log(input.value); // 'new default'
console.log(input.defaultValue); // 'new default'
// Here comes the mode switch:
input.value = 'hello!';
console.log(input.getAttribute('value')); // 'new default'
console.log(input.value); // 'hello!'
console.log(input.defaultValue); // 'new default'
input.setAttribute('value', 'another new default');
console.log(input.getAttribute('value')); // 'another new default'
console.log(input.value); // 'hello!'
console.log(input.defaultValue); // 'another new default'
</noscript>
По мнению автора, это правильная логика. Атрибуты предназначены для настройки, тогда как свойства уже могут иметь состояние.
https://jakearchibald.com/2024/attributes-vs-properties/
Jakearchibald
HTML attributes vs DOM properties
They're completely different, but often coupled.
👍12❤1
React 19 Beta
Вышел релиз React 19 Beta.
Что нового:
- Новый хук useActionState (вместо useFormState) для выполнения действий. Может использовать для выполнения асинхронных действий, также его можно передавать в action у тега form. Пример:
- Новый хук useFormStatus для получения статуса формы, в которой он находится. Хук получает статус родительской формы, как если бы форма была провайдером контекста.
- Новый хук useOptimistic для реализации оптимистичного UI. Пример:
Хук useOptimistic вернет optimisticName, пока выполняется запрос updateName. Когда запрос завершится или произойдет ошибка, React переключится обратно на значение currentName.
- Новый хук use для чтения промисов.
Улучшения:
- Теперь ref передается как проп в функциональных компонентах. Функция forwardRef будет не нужна и станет deprecated.
- Показ diff для ошибок гидратации.
- Контекст как провайдер, т.е. <Context> вместо <Context.Provider>.
- В колбеке ref можно вернуть функцию очистки, которая будет вызываться при размонтировании компонента. Раньше React при размонтировании вызывал ref повторно с null. Если ref возвращает функцию очистки, то React пропускает повторный вызов ref с null при размонтировании.
- Появилась поддержка предварительной загрузки ресурсов. Новое API в React DOM для предзагрузки:
https://react.dev/blog/2024/04/25/react-19
Вышел релиз React 19 Beta.
Что нового:
- Новый хук useActionState (вместо useFormState) для выполнения действий. Может использовать для выполнения асинхронных действий, также его можно передавать в action у тега form. Пример:
function ChangeName({ name, setName }) {
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get("name"));
if (error) {
return error;
}
redirect("/path");
return null;
},
null,
);
return (
<form action={submitAction}>
{/* --snip-- */}
</form>
);
}
- Новый хук useFormStatus для получения статуса формы, в которой он находится. Хук получает статус родительской формы, как если бы форма была провайдером контекста.
- Новый хук useOptimistic для реализации оптимистичного UI. Пример:
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async formData => {
const newName = formData.get("name");
setOptimisticName(newName);
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
Хук useOptimistic вернет optimisticName, пока выполняется запрос updateName. Когда запрос завершится или произойдет ошибка, React переключится обратно на значение currentName.
- Новый хук use для чтения промисов.
Улучшения:
- Теперь ref передается как проп в функциональных компонентах. Функция forwardRef будет не нужна и станет deprecated.
- Показ diff для ошибок гидратации.
- Контекст как провайдер, т.е. <Context> вместо <Context.Provider>.
- В колбеке ref можно вернуть функцию очистки, которая будет вызываться при размонтировании компонента. Раньше React при размонтировании вызывал ref повторно с null. Если ref возвращает функцию очистки, то React пропускает повторный вызов ref с null при размонтировании.
- Появилась поддержка предварительной загрузки ресурсов. Новое API в React DOM для предзагрузки:
import { prefetchDNS, preconnect, preload, preinit } from 'react-dom'
function MyComponent() {
preinit('https://.../path/to/some/noscript.js', {as: 'noscript' }) // loads and executes this noscript eagerly
preload('https://.../path/to/font.woff', { as: 'font' }) // preloads this font
preload('https://.../path/to/stylesheet.css', { as: 'style' }) // preloads this stylesheet
prefetchDNS('https://...') // when you may not actually request anything from this host
preconnect('https://...') // when you will request something but aren't sure what
}
https://react.dev/blog/2024/04/25/react-19
react.dev
React v19 – React
The library for web and native user interfaces
🔥14👍4❤1
Получение текущего URL в серверных компонентах в Next.js
В серверных компонентах нельзя использовать хуки. Поэтому, чтобы получить текущий URL, надо использовать middleware, который запишет текущий URL в headers:
Пример компонента, который получает объект с заголовками через функцию headers:
https://www.propelauth.com/post/getting-url-in-next-server-components
В серверных компонентах нельзя использовать хуки. Поэтому, чтобы получить текущий URL, надо использовать middleware, который запишет текущий URL в headers:
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
// Добавляем новый заголовок x-current-path, который получим в компоненте
const headers = new Headers(request.headers);
headers.set("x-current-path", request.nextUrl.pathname);
return NextResponse.next({ headers });
}
export const config = {
matcher: [
// Сопоставим со всеми маршрутами кроме статических файлов и API
"/((?!api|_next/static|_next/image|favicon.ico).*)",
],
};
Пример компонента, который получает объект с заголовками через функцию headers:
import { headers } from "next/headers";
export default async function Sidebar() {
const headerList = headers();
const pathname = headerList.get("x-current-path");
// ...etc
}
https://www.propelauth.com/post/getting-url-in-next-server-components
Propelauth
Getting the Current URL in Next.js Server Components
You are writing a server component using the new(ish) App Router in Next.js, when you realize you need to get the current URL’s path. This could be for a sidebar, navbar, or even just breadcrumbs at the top of your page.
Should be pretty easy, a quick Google…
Should be pretty easy, a quick Google…
👍20👎7❤1