В 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
Частичный пре-рендер с помощью Bun
Bun представил идею макросов в JavaScript. Макрос в Bun – это механизм запуска JS функций во время сборки. Значение, возвращаемое этими функциями, напрямую встраивается в бандл.
Арал Рока в своем блоге предложил идею пре-рендера статических компонентов во время сборки с помощью Bun макросов. Таким образом, на одной странице могут отображаться одновременно статические компоненты, отрендеренные во время сборки, и динамические компоненты, отрендеренные в runtime.
Автор предлагает использовать Bun плагин prerender-macro. Пример кода, который использует макросы:
С помощью плагина код выше преобразуется в следующий промежуточный код:
После чего этот код возвращается в Bun для транспиляции и запуска макросов, которые преобразуют статический компонент в HTML строку во время сборки.
Автор обещает, что при использовании частичного пре-рендера компонентов будет меньше размер бандла и более быстрый runtime рендер страницы.
https://aralroca.com/blog/partial-prerendering
Bun представил идею макросов в JavaScript. Макрос в Bun – это механизм запуска JS функций во время сборки. Значение, возвращаемое этими функциями, напрямую встраивается в бандл.
Арал Рока в своем блоге предложил идею пре-рендера статических компонентов во время сборки с помощью Bun макросов. Таким образом, на одной странице могут отображаться одновременно статические компоненты, отрендеренные во время сборки, и динамические компоненты, отрендеренные в runtime.
Автор предлагает использовать Bun плагин prerender-macro. Пример кода, который использует макросы:
import StaticComponent from "@/static-component" with { type: "prerender" };
import DynamicComponent from "@/dynamic-component";
return (
<>
<StaticComponent foo="bar" />
<DynamicComponent />
</>
);
С помощью плагина код выше преобразуется в следующий промежуточный код:
import { prerender } from "prerender-macro/prerender" with { "type": "macro" };
import DynamicComponent from "@/dynamic-component";
// ...
return (
<>
{prerender({
componentPath: "@/static-component",
componentModuleName: "default",
componentProps: { foo: "bar" },
})}
<DynamicComponent />
</>
);
После чего этот код возвращается в Bun для транспиляции и запуска макросов, которые преобразуют статический компонент в HTML строку во время сборки.
Автор обещает, что при использовании частичного пре-рендера компонентов будет меньше размер бандла и более быстрый runtime рендер страницы.
https://aralroca.com/blog/partial-prerendering
Aralroca
Power of Partial Prerendering with Bun
Unlock the power of partial pre-rendering with Bun, optimizing web application performance and package size effortlessly.
👍17
Headless 2.0
Headless – это библиотека компонентов без стилей, но с хорошей поддержкой доступности. Разрабатывается командой Tailwind CSS.
Вышел релиз мажорной версии Headless 2.0. Что нового:
- Интеграция Floating UI для правильного позиционирования якоря дропдауна.
- Новый компонент checkbox.
- Обертки над нативными элементами формы: Fieldset, Field, Label, Input, Select и другие. Внутри них связываются лейбл и поле через ID, добавляются aria-* атрибуты и т.д. Пример использования:
- Добавили data атрибуты к элементам для hover, active, focus состояний. Под капотом используются хуки из React Aria. Может пригодиться для стилизации элементов.
- Интеграция TanStack Virtual для виртуализации списка в Combobox.
- Новый сайт и улучшенная документация.
https://tailwindcss.com/blog/headless-ui-v2
Headless – это библиотека компонентов без стилей, но с хорошей поддержкой доступности. Разрабатывается командой Tailwind CSS.
Вышел релиз мажорной версии Headless 2.0. Что нового:
- Интеграция Floating UI для правильного позиционирования якоря дропдауна.
- Новый компонент checkbox.
- Обертки над нативными элементами формы: Fieldset, Field, Label, Input, Select и другие. Внутри них связываются лейбл и поле через ID, добавляются aria-* атрибуты и т.д. Пример использования:
import { Denoscription, Field, Input, Label } from '@headlessui/react'
function Example({ disabled }) {
return (
<Field disabled={disabled}>
<Label>Name</Label>
<Input name="your_name" />
<Denoscription>Use your real name so people will recognize you.</Denoscription>
</Field>
)
}
- Добавили data атрибуты к элементам для hover, active, focus состояний. Под капотом используются хуки из React Aria. Может пригодиться для стилизации элементов.
- Интеграция TanStack Virtual для виртуализации списка в Combobox.
- Новый сайт и улучшенная документация.
https://tailwindcss.com/blog/headless-ui-v2
Tailwindcss
Headless UI v2.0 for React
We just released Headless UI v2.0 for React which includes a ton of new stuff including built-in anchor positioning, a new headless checkbox component, HTML form components and more!
👍24
Почему глобальный патчинг вреден
В блоге Артема Захарченко вышла статья про вред глобального патчинга API, при котором меняется его поведение. В качестве примера автор приводит fetch в Next.js и Bun:
В обоих случаях меняется стандартное поведение функции fetch глобально. Глобальный патчинг API не только влечет за собой затраты, которые влияют как на пользователей, так и на мейнтенеров, но и вредит другим API и даже самому языку. Как вредит глобальный патчинг:
- Поддержка. Когда вы что-то меняете глобально, вы изменяете то, чем не владеете. Поддержка того, чем ты не владеешь, сложно. Например, вы изменили глобально fetch, чтобы он умел дедуплицировать запросы. Что будет, если вы будете использовать сторонний модуль, в который также встроен механизм дедупликации. Эти два поведения будут конфликтовать, вызывая проблемы, которые сложно отладить.
- Предсказуемость. Для того, чтобы CSS одинаково работал во всех браузерах были созданы стандарты стилей. Существуют стандарты Web API для кросс-браузерной совместимости. Также Node.js хорошо поддерживает Web API, предоставляя fetch, ReadableStream, WebSocket и т.д. Таким образом, использование одного API в разных средах предсказуемо. Поэтому изменение поведения fetch в Next.js вредит предсказуемости, потому что это поведение будет работать только в Next.js.
- Обучение. Хорошее API должно обучать. Оно должно быть приближено к существующим стандартам.
Взамен существующему глобальному патчингу в bun, можно использовать существующий API fetch и Request и создать отдельную функцию проксирования:
Как можно сделать в Next.js:
Тем не менее, Next.js идет в сторону отдельной функции для кэширования запросов unstable_cache вместо глобального патчинга fetch.
https://kettanaito.com/blog/why-patching-globals-is-harmful
В блоге Артема Захарченко вышла статья про вред глобального патчинга API, при котором меняется его поведение. В качестве примера автор приводит fetch в Next.js и Bun:
// Next.js добавил кэширование и возможность сброса кэша
fetch('https://kettanaito.com', {
next: { revalidate: 10 },
})
// Bun добавил возможность проксировать запрос
fetch('https://redd.one', {
proxy: 'https://kettanaito.com',
})
В обоих случаях меняется стандартное поведение функции fetch глобально. Глобальный патчинг API не только влечет за собой затраты, которые влияют как на пользователей, так и на мейнтенеров, но и вредит другим API и даже самому языку. Как вредит глобальный патчинг:
- Поддержка. Когда вы что-то меняете глобально, вы изменяете то, чем не владеете. Поддержка того, чем ты не владеешь, сложно. Например, вы изменили глобально fetch, чтобы он умел дедуплицировать запросы. Что будет, если вы будете использовать сторонний модуль, в который также встроен механизм дедупликации. Эти два поведения будут конфликтовать, вызывая проблемы, которые сложно отладить.
- Предсказуемость. Для того, чтобы CSS одинаково работал во всех браузерах были созданы стандарты стилей. Существуют стандарты Web API для кросс-браузерной совместимости. Также Node.js хорошо поддерживает Web API, предоставляя fetch, ReadableStream, WebSocket и т.д. Таким образом, использование одного API в разных средах предсказуемо. Поэтому изменение поведения fetch в Next.js вредит предсказуемости, потому что это поведение будет работать только в Next.js.
- Обучение. Хорошее API должно обучать. Оно должно быть приближено к существующим стандартам.
Взамен существующему глобальному патчингу в bun, можно использовать существующий API fetch и Request и создать отдельную функцию проксирования:
import { proxy } from 'bun'
const proxiedRequest = proxy(proxyUrl, new Request(url, options))
await fetch(proxiedRequest)
Как можно сделать в Next.js:
import { withCache } from 'next'
const request = new Request('https://kettanaito.com')
fetch(withCache(request, { revalidate: 10 }))
Тем не менее, Next.js идет в сторону отдельной функции для кэширования запросов unstable_cache вместо глобального патчинга fetch.
https://kettanaito.com/blog/why-patching-globals-is-harmful
kettanaito.com
Artem Zakharchenko's personal blog.
👍17🔥1
Гайд по SolidJS для React разработчиков
Код на Solid очень похож на React, т.к. оба используют JSX. Однако Solid работает по-другому и важно знать эти отличия. Автор в своей статье написал об отличиях Solid и React:
- Компонент и рендеринг. В React компонент - это функция, которая является частью цикла рендеринга. Компонент возвращает объект, который описывает что должно быть отрендерено. Компонент в Solid - это функция, которая вызывается один раз для создания DOM узла. Можно сказать, что в функция возвращается шаблон.
- Реактивность. В React для реактивности используется useState и контекст, изменение которых вызывает ре-рендер всего компонента. В Solid для реактивности используется createSignal и при изменении значения сигнала ре-рендерится DOM узел, использующий сигнал.
Также автор привел популярные ошибки, с которыми сталкиваются при переходе на Solid. Например, в Solid при деструктуризации пропсов теряется реактивность:
Связано это с тем, что Solid использует Proxy для передачи пропсов. Далее, в тех местах где используется проп через
https://www.stashpad.com/blog/react-developer-guide-to-solid-js
Код на Solid очень похож на React, т.к. оба используют JSX. Однако Solid работает по-другому и важно знать эти отличия. Автор в своей статье написал об отличиях Solid и React:
- Компонент и рендеринг. В React компонент - это функция, которая является частью цикла рендеринга. Компонент возвращает объект, который описывает что должно быть отрендерено. Компонент в Solid - это функция, которая вызывается один раз для создания DOM узла. Можно сказать, что в функция возвращается шаблон.
- Реактивность. В React для реактивности используется useState и контекст, изменение которых вызывает ре-рендер всего компонента. В Solid для реактивности используется createSignal и при изменении значения сигнала ре-рендерится DOM узел, использующий сигнал.
Также автор привел популярные ошибки, с которыми сталкиваются при переходе на Solid. Например, в Solid при деструктуризации пропсов теряется реактивность:
function Message(props) {
const { text } = props;
return <div>{ text }</div>
}
Связано это с тем, что Solid использует Proxy для передачи пропсов. Далее, в тех местах где используется проп через
. или [], используется get функция объекта Proxy, в котором возвращается сигнал. https://www.stashpad.com/blog/react-developer-guide-to-solid-js
👍20❤3
Как работает RSC
В блоге Smashing Magazine автор Лазарь Николов написал подробную статью о том, как работает React Server Components и как это отражается на загрузке страницы. Перед погружением в RSC автор напомнил, как работают CSR, SSR, SSG и ISR, какие у них преимущества и недостатки.
Автор рассказал как теорию, так и показал на практике, как работает RSC. О чем пишет автор:
- жизненный цикл рендеринга RSC в Next.js;
- что такое RSC payload и как его читать;
- как происходит стриминг HTML по чанкам;
- как в HTML вставляется фолбек Suspense и происходит его замена на отрендеренный компонент.
https://www.smashingmagazine.com/2024/05/forensics-react-server-components/
В блоге Smashing Magazine автор Лазарь Николов написал подробную статью о том, как работает React Server Components и как это отражается на загрузке страницы. Перед погружением в RSC автор напомнил, как работают CSR, SSR, SSG и ISR, какие у них преимущества и недостатки.
Автор рассказал как теорию, так и показал на практике, как работает RSC. О чем пишет автор:
- жизненный цикл рендеринга RSC в Next.js;
- что такое RSC payload и как его читать;
- как происходит стриминг HTML по чанкам;
- как в HTML вставляется фолбек Suspense и происходит его замена на отрендеренный компонент.
https://www.smashingmagazine.com/2024/05/forensics-react-server-components/
Smashing Magazine
The Forensics Of React Server Components (RSCs) — Smashing Magazine
React Server Components (RSCs) combine the best of client-side rendering, and author Lazar Nikolov thoroughly examines how we got here with a deep look at the impact that RSCs have on the page load timeline.
👍15👎1🔥1
Создание бесконечного скролла и E2E тест к нему
Бесконечный скролл позволяет загружать данные на страницу поэтапно, в процессе скролла пользователя. В блоге Wanago автор описал как работает бесконечный скролл с Intersection Observer для постраничной загрузки данных.
Для тестирования функционала используется библиотека Playwright. Узнать количество загруженных записей можно с помощью функции toHaveCount, а проскроллить до элемента-якоря можно с помощью scrollIntoViewIfNeeded:
https://wanago.io/2024/04/29/react-infinite-scrolling-e2e-playwright/
Бесконечный скролл позволяет загружать данные на страницу поэтапно, в процессе скролла пользователя. В блоге Wanago автор описал как работает бесконечный скролл с Intersection Observer для постраничной загрузки данных.
Для тестирования функционала используется библиотека Playwright. Узнать количество загруженных записей можно с помощью функции toHaveCount, а проскроллить до элемента-якоря можно с помощью scrollIntoViewIfNeeded:
import { expect, test } from '@playwright/test';
test.describe('The Posts page', () => {
test.describe('when visited', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
// ...
test.describe('and when the user scrolls down', () => {
test('should contain forty posts', async ({ page }) => {
const posts = page.getByTestId('post');
await expect(posts).toHaveCount(20);
await page.getByTestId('loader').scrollIntoViewIfNeeded();
await expect(posts).toHaveCount(40);
});
});
});
});
https://wanago.io/2024/04/29/react-infinite-scrolling-e2e-playwright/
Marcin Wanago Blog - JavaScript, both frontend and backend
Implementing infinite scrolling with React. Writing E2E tests with Playwright
We implement infinite scrolling using React and the Intersection Observer. We also write End-to-End (E2E) test with Playwright.
👍13🔥7
React Compiler вышел в open source
Появилась документация по React Compiler, в которой предлагают попробовать экспериментальную версию компилятора. Для того чтобы компилятор оптимизировал компонент, он должен соблюдать условия:
- Валидный JS без ошибок.
- Проверяет nullable/опциональные свойства перед доступом к ним. Пример:
- Соблюдает «Правила React».
Для того чтобы соблюдать «Правила React», появился ESLint плагин eslint-plugin-react-compiler. Этот плагин можно использовать без компилятора.
React Compiler можно добавить в существующий проект на Vite, Next и т.д. через babel-плагин. У плагина есть настройка для фильтрации компилируемых файлов, поэтому можно запустить компилятор только на часть проекта:
Для того чтобы определить, что компонент оптимизирован компилятором, React Devtools (v5.0+) добавляет бейдж «Memo ✨» рядом с компонентом.
Также команда React создала песочницу, в которой можно увидеть, как компилируется компонент – React Compiler Playground.
https://react.dev/learn/react-compiler
Появилась документация по React Compiler, в которой предлагают попробовать экспериментальную версию компилятора. Для того чтобы компилятор оптимизировал компонент, он должен соблюдать условия:
- Валидный JS без ошибок.
- Проверяет nullable/опциональные свойства перед доступом к ним. Пример:
if (object.nullableProperty) { object.nullableProperty.foo }. Чтобы это условие выполнялось в TS, надо включить strictNullChecks.- Соблюдает «Правила React».
Для того чтобы соблюдать «Правила React», появился ESLint плагин eslint-plugin-react-compiler. Этот плагин можно использовать без компилятора.
React Compiler можно добавить в существующий проект на Vite, Next и т.д. через babel-плагин. У плагина есть настройка для фильтрации компилируемых файлов, поэтому можно запустить компилятор только на часть проекта:
const ReactCompilerConfig = {
sources: (filename) => {
return filename.indexOf('src/path/to/dir') !== -1;
},
};
Для того чтобы определить, что компонент оптимизирован компилятором, React Devtools (v5.0+) добавляет бейдж «Memo ✨» рядом с компонентом.
Также команда React создала песочницу, в которой можно увидеть, как компилируется компонент – React Compiler Playground.
https://react.dev/learn/react-compiler
react.dev
React Compiler – React
The library for web and native user interfaces
👍24👎5❤1
Предпочитайте несколько композиций
В своем блоге Кайл Шевлин предложил, что, если компонент имеет ограниченный набор вариантов обработки, то будет удобнее иметь четкие отдельные ветки if/else для каждого случая, а не распространять условные выражения повсюду в JSX. Сравните два примера:
Хоть и во втором примере больше кода, он более читабельный. По мере усложнения проекта уменьшается скорость чтения кода, наличие условных операторов особенно усложняет понимание кода. Во втором примере вместо трех условных операторов используется только один условный оператор. Код стал понятнее и менее подвержен багам.
https://kyleshevlin.com/prefer-multiple-compositions/
В своем блоге Кайл Шевлин предложил, что, если компонент имеет ограниченный набор вариантов обработки, то будет удобнее иметь четкие отдельные ветки if/else для каждого случая, а не распространять условные выражения повсюду в JSX. Сравните два примера:
function VideoControls() {
const { isPlaying, play, pause } = useVideoControls()
return (
<Button onPress={isPlaying ? pause : play}>
<Icon name={isPlaying ? 'pause' : 'play'} />
<Button.Text>{isPlaying ? 'Pause' : 'Play'}</Button.Text>
</Button>
)
}
function VideoControls() {
const { isPlaying, play, pause } = useVideoControls()
if (isPlaying) {
return (
<Button onPress={pause}>
<Icon name="pause" />
<Button.Text>Pause</Button.Text>
</Button>
)
}
return (
<Button onPress={play}>
<Icon name="play" />
<Button.Text>Play</Button.Text>
</Button>
)
}
Хоть и во втором примере больше кода, он более читабельный. По мере усложнения проекта уменьшается скорость чтения кода, наличие условных операторов особенно усложняет понимание кода. Во втором примере вместо трех условных операторов используется только один условный оператор. Код стал понятнее и менее подвержен багам.
https://kyleshevlin.com/prefer-multiple-compositions/
Kyle Shevlin's Blog
Prefer Multiple Compositions | Kyle Shevlin
The flexibility of JavaScript and React means there are lots of ways to achieve the same result. Let's consider why we might choose one way over another when it comes to React. Specifically, when to choose a more verbose solution with composition over the…
👍34👎4
Влияние Styled Components на время ответа SSR
В своем блоге Эндрю Левин написал статью о результатах измерения перфоманса SSR приложения со Styled Components. Styled Components замедляет работу SSR приложения, но насколько – нужно было измерить. Для того чтобы сделать замеры автор использовал неминифицированную копию Styled Components и собрал трассировку CPU во время рендера SSR приложения.
В результате, на рендер дерева компонентов ушло 250мс, из которых 117мс потрачено на генерацию и внедрение Styled Components. Таким образом, ~47% времени SSR было потрачено на Styled Components.
https://blog.levineandrew.com/quantifying-the-impact-of-styled-components-on-server-response-times
В своем блоге Эндрю Левин написал статью о результатах измерения перфоманса SSR приложения со Styled Components. Styled Components замедляет работу SSR приложения, но насколько – нужно было измерить. Для того чтобы сделать замеры автор использовал неминифицированную копию Styled Components и собрал трассировку CPU во время рендера SSR приложения.
В результате, на рендер дерева компонентов ушло 250мс, из которых 117мс потрачено на генерацию и внедрение Styled Components. Таким образом, ~47% времени SSR было потрачено на Styled Components.
https://blog.levineandrew.com/quantifying-the-impact-of-styled-components-on-server-response-times
Andrew Levine's Blog
Quantifying the Impact of Styled Components on Server Response Times
Working off of a suspicion, I spent some time at work trying to properly attribute the amount of time spent during SSR to Styled Components
👍17
Автоматическая инвалидация запросов в React Query
Reacy Query позволяет инвалидировать запросы в ручном режиме
Доминик Дорфмайстер в своем блоге показал несколько способов как сделать автоматическую инвалидацию запросов. Инвалидацию можно делать в глобальном обработчике onSuccess кэша. Пример использования поля meta, в котором можно указать список полей для инвалидации:
https://tkdodo.eu/blog/automatic-query-invalidation-after-mutations
Reacy Query позволяет инвалидировать запросы в ручном режиме
queryClient.invalidateQueries({ queryKey: ['todos'] }). В библиотеке не добавляли встроенные способы автоматической инвалидации по причине того, что это ухудшает гибкость. Ведь в разных ситуациях может потребоваться разная инвалидация: во время onSuccess или onSettled, должна ли мутация ждать инвалидацию. Ожидание инвалидации приводит к тому, что мутация остается в состоянии ожидания pending = true.Доминик Дорфмайстер в своем блоге показал несколько способов как сделать автоматическую инвалидацию запросов. Инвалидацию можно делать в глобальном обработчике onSuccess кэша. Пример использования поля meta, в котором можно указать список полей для инвалидации:
import { matchQuery } from '@tanstack/react-query'
const queryClient = new QueryClient({
mutationCache: new MutationCache({
onSuccess: (_data, _variables, _context, mutation) => {
queryClient.invalidateQueries({
predicate: (query) =>
// invalidate all matching tags at once
// or everything if no meta is provided
mutation.meta?.invalidates?.some((queryKey) =>
matchQuery({ queryKey }, query)
) ?? true,
})
},
}),
})
// usage:
useMutation({
mutationFn: updateLabel,
meta: {
invalidates: [['issues'], ['labels']],
},
})
https://tkdodo.eu/blog/automatic-query-invalidation-after-mutations
tkdodo.eu
Automatic Query Invalidation after Mutations
Even though there is nothing built into React Query, it doesn't need a lot of code to implement automatic query invalidation in user-land thanks to the global cache callbacks.
👍9❤2🔥1
useCallback и утечки памяти
В своем блоге Кевин Шинер поделился об опыте поиска утечек памяти при использовании useCallback. Рассмотрим пример:
Если попеременно вызывать handleClickA и handleClickB, то произойдет бесконечная утечка памяти. Например, при вызове функции handleClickA будет заново создан колбек только для handleClickA, т.к. поменялся countA. Для handleClickB останется старый колбек с текущей областью видимости. Новая область видимости будет хранить ссылку на новый колбек handleClickA и старый колбек handleClickB, внутри которых старая область видимости компонента.
Основная проблема связана с тем, что в одном компоненте разные useCallback могут ссылаться друг на друга и на другие данные через замыкания. Эти замыкания сохраняются в памяти до тех пор, пока useCallback не будет создан заново. Поэтому наличие в компоненте более одного useCallback затрудняет понимание того, что хранится в памяти.
Для того чтобы избежать утечек памяти в компонентах, придерживайтесь следующих советов:
- Пишите небольшие компоненты и используйте кастомные хуки.
- Избегайте захвата других замыканий. Это может произойти, если вы вызываете из одного useCallback другой useCallback.
- Избегайте мемоизации, когда в этом нет необходимости.
- Используйте useRef для хранения больших объектов.
https://schiener.io/2024-03-03/react-closures
В своем блоге Кевин Шинер поделился об опыте поиска утечек памяти при использовании useCallback. Рассмотрим пример:
import { useState, useCallback } from "react";
class BigObject {
public readonly data = new Uint8Array(1024 * 1024 * 10);
}
export const App = () => {
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
const bigData = new BigObject(); // 10MB of data
const handleClickA = useCallback(() => {
setCountA(countA + 1);
}, [countA]);
const handleClickB = useCallback(() => {
setCountB(countB + 1);
}, [countB]);
// …
};
Если попеременно вызывать handleClickA и handleClickB, то произойдет бесконечная утечка памяти. Например, при вызове функции handleClickA будет заново создан колбек только для handleClickA, т.к. поменялся countA. Для handleClickB останется старый колбек с текущей областью видимости. Новая область видимости будет хранить ссылку на новый колбек handleClickA и старый колбек handleClickB, внутри которых старая область видимости компонента.
Основная проблема связана с тем, что в одном компоненте разные useCallback могут ссылаться друг на друга и на другие данные через замыкания. Эти замыкания сохраняются в памяти до тех пор, пока useCallback не будет создан заново. Поэтому наличие в компоненте более одного useCallback затрудняет понимание того, что хранится в памяти.
Для того чтобы избежать утечек памяти в компонентах, придерживайтесь следующих советов:
- Пишите небольшие компоненты и используйте кастомные хуки.
- Избегайте захвата других замыканий. Это может произойти, если вы вызываете из одного useCallback другой useCallback.
- Избегайте мемоизации, когда в этом нет необходимости.
- Используйте useRef для хранения больших объектов.
https://schiener.io/2024-03-03/react-closures
www.schiener.io
Sneaky React Memory Leaks: How `useCallback` and closures can bite you
Avoid performance issues caused by memory leaks in your React applications. I will show you how closures and the `useCallback` hook can lead to memory leaks and what you can do to avoid them.
👍19❤2