Новый хук useOptimistic
В React Canary появился хук useOptimistic. Он позволяет показывать другой стейт, пока выполняется асинхронная функция. Пример:
Разберем как работает этот хук:
optimisticState - это оптимистическое состояние, по умолчанию оно соотвествует state.
addOptimistic - это функция диспатчер, которая принимает optimisticValue и будет вызывать updateFn.
state - это источник правды, если state был изменен, то optimisticState будет установлен в state.
updateFn - это функция мутации, которая принимает текущее состояние и значение optimisticValue и возвращает новое состояние optimisticState.
Чтобы на UI пометить текущий стейт как оптимистичный, можно в updateFn возвращать новый объект с полем sending: true и при рендере показывать сообщение об отправке при наличии sending. Пример:
https://react.dev/reference/react/useOptimistic
В React Canary появился хук useOptimistic. Он позволяет показывать другой стейт, пока выполняется асинхронная функция. Пример:
function AppContainer() {
const [optimisticState, addOptimistic] = useOptimistic(
state,
// updateFn
(currentState, optimisticValue) => {
// merge and return new state
// with optimistic value
}
);
}
Разберем как работает этот хук:
optimisticState - это оптимистическое состояние, по умолчанию оно соотвествует state.
addOptimistic - это функция диспатчер, которая принимает optimisticValue и будет вызывать updateFn.
state - это источник правды, если state был изменен, то optimisticState будет установлен в state.
updateFn - это функция мутации, которая принимает текущее состояние и значение optimisticValue и возвращает новое состояние optimisticState.
Чтобы на UI пометить текущий стейт как оптимистичный, можно в updateFn возвращать новый объект с полем sending: true и при рендере показывать сообщение об отправке при наличии sending. Пример:
function Thread({ messages, sendMessage }) {
const formRef = useRef();
async function formAction(formData) {
addOptimisticMessage(formData.get("message"));
formRef.current.reset();
await sendMessage(formData);
}
const [optimisticMessages, addOptimisticMessage] = useOptimistic(
messages,
(state, newMessage) => [
...state,
{
text: newMessage,
sending: true
}
]
);
return (
<>
{optimisticMessages.map((message, index) => (
<div key={index}>
{message.text}
{!!message.sending && <small> (Sending...)</small>}
</div>
))}
<form action={formAction} ref={formRef}>
<input type="text" name="message" placeholder="Hello!" />
<button type="submit">Send</button>
</form>
</>
);
}
https://react.dev/reference/react/useOptimistic
react.dev
useOptimistic – React
The library for web and native user interfaces
🔥12👍7
Релиз TypeScript 5.3
Вышла новая версия TypeScript 5.3. Основные изменения:
- Атрибуты импорта. Их можно использовать для указания ожидаемого формата модуля во время выполнения. TS не проверяет содержимое атрибутов, указанный тип и т.д. и оставляет эту проверку браузеру или другой среде выполнения. Пример использования:
- Стабильная поддержка resolution-mode для импорта типов. Можно импортировать типы как будто бы импорт происходит с помощью require() или import. Пример:
- Улучшение сужение типов. Если использовать switch (true), то можно сужать тип в условии case. Пример:
TS начал понимать сужение типов при сравнении с true/false. Пример:
https://devblogs.microsoft.com/typenoscript/announcing-typenoscript-5-3/
Вышла новая версия TypeScript 5.3. Основные изменения:
- Атрибуты импорта. Их можно использовать для указания ожидаемого формата модуля во время выполнения. TS не проверяет содержимое атрибутов, указанный тип и т.д. и оставляет эту проверку браузеру или другой среде выполнения. Пример использования:
// Модуль будет интерпретироваться как JSON
import obj from "./something.json" with { type: "json" };
- Стабильная поддержка resolution-mode для импорта типов. Можно импортировать типы как будто бы импорт происходит с помощью require() или import. Пример:
// Resolve `pkg` as if we were importing with a
require()
import type { TypeFromRequire } from "pkg" with {
"resolution-mode": "require"
};
- Улучшение сужение типов. Если использовать switch (true), то можно сужать тип в условии case. Пример:
switch (true) {
case typeof x === "string":
// 'x' is a 'string' here
console.log(x.toUpperCase());
}
TS начал понимать сужение типов при сравнении с true/false. Пример:
function isA(x: MyType): x is A {
return "a" in x;
}
if (isA(x) === true) {
console.log(x.a); // x is A
}
https://devblogs.microsoft.com/typenoscript/announcing-typenoscript-5-3/
Microsoft News
Announcing TypeScript 5.3
Today we’re excited to announce the release of TypeScript 5.3! If you’re not familiar with TypeScript, it’s a language that adds type syntax to JavaScript to bring type-checking. Type-checking can catch all sorts of issues like typos and forgetting to check…
👍18
Как работает React
Проект, в котором написаны статьи с глубоким погружением во внутреннее устройство React. Это не курс и не руководство по React, а объяснение кодовой базы React. Цель проекта – объяснить основные концепции, показать важные «пути кода», чтобы комфортно разбираться в исходном коде React.
Проект еще находится в стадии заполнения, но уже сейчас можно почитать:
- Как создается приложение и работает функция createRoot.
- Как работает рабочий цикл рендера.
- Какая модель хуков и как они работают и т.д.
https://incepter.github.io/how-react-works/docs/intro
Проект, в котором написаны статьи с глубоким погружением во внутреннее устройство React. Это не курс и не руководство по React, а объяснение кодовой базы React. Цель проекта – объяснить основные концепции, показать важные «пути кода», чтобы комфортно разбираться в исходном коде React.
Проект еще находится в стадии заполнения, но уже сейчас можно почитать:
- Как создается приложение и работает функция createRoot.
- Как работает рабочий цикл рендера.
- Какая модель хуков и как они работают и т.д.
https://incepter.github.io/how-react-works/docs/intro
incepter.github.io
Introduction | How React Works
Hi, and welcome to this blog post series aiming to explain how react works
👍24🔥5
Как создать ESM+CJS библиотеку React
Гайд по созданию собственной React библиотеки, которая поддерживает экспорт в ESM и CommonJS. При создании библиотеки на JS/TS стоит учитывать следующие моменты:
- Разные системы модулей (ESM, CJS, IIFE, UMD и т.д.), которые не совместимы с друг другом. Поддержка всех не обязательна, но будет хорошей идеей поддерживать ESM и CJS.
- Разные окружения: браузер, Node.js, Deno, Bun.
- В случае JS, возможно придется ориентироваться на более старые версии языка.
- В случае TS, нужно компилировать код в JS перед публикацией, чтобы он работал в проектах на чистом JS.
- Для TS проекта надо отдельно генерировать типы и source map для упрощения отладки.
В своей статье автор представил конфигурацию проекта на Rollup, показал как настроить tsconfig.json с комментарием каждого значения и описал package.json с блоком экспорта.
https://blog.coderspirit.xyz/blog/2023/09/15/create-a-react-component-lib/
Гайд по созданию собственной React библиотеки, которая поддерживает экспорт в ESM и CommonJS. При создании библиотеки на JS/TS стоит учитывать следующие моменты:
- Разные системы модулей (ESM, CJS, IIFE, UMD и т.д.), которые не совместимы с друг другом. Поддержка всех не обязательна, но будет хорошей идеей поддерживать ESM и CJS.
- Разные окружения: браузер, Node.js, Deno, Bun.
- В случае JS, возможно придется ориентироваться на более старые версии языка.
- В случае TS, нужно компилировать код в JS перед публикацией, чтобы он работал в проектах на чистом JS.
- Для TS проекта надо отдельно генерировать типы и source map для упрощения отладки.
В своей статье автор представил конфигурацию проекта на Rollup, показал как настроить tsconfig.json с комментарием каждого значения и описал package.json с блоком экспорта.
https://blog.coderspirit.xyz/blog/2023/09/15/create-a-react-component-lib/
👍13🔥1
Типы событий в React и TypeScript
Когда работаешь с React и TypeScript можно столкнуться со следующей ошибкой:
Не всегда ясно, какой тип стоит присвоить
1. Навести и узнать тип самому, потом переиспользовать его в объявлении колбека:
2. Встроить функцию и узнать тип аргумента:
3. Использовать тип из React для получения пропсов элемента React.ComponentProps:
4. Написать свой тип-помощник для определения типа аргументов:
https://www.totaltypenoscript.com/event-types-in-react-and-typenoscript
Когда работаешь с React и TypeScript можно столкнуться со следующей ошибкой:
const onChange = (e) => {}; // Parameter 'e' implicitly has an 'any' type.
<input onChange={onChange} />;
Не всегда ясно, какой тип стоит присвоить
e в функции onChange. Это может произойти в onClick, onSubmit и другом обработчике DOM событий. Есть несколько вариантов решения проблемы: 1. Навести и узнать тип самому, потом переиспользовать его в объявлении колбека:
// Узнать тип
<input onChange={onChange} /> // React.ChangeEventHandler<HTMLInputElement> | undefined
// Использование
const onChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
console.log(e);
};
2. Встроить функцию и узнать тип аргумента:
// Узнать тип аргумента
<input onChange={(e) => {}} /> // React.ChangeEvent<HTMLInputElement>
// Использование
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
console.log(e);
};
3. Использовать тип из React для получения пропсов элемента React.ComponentProps:
const onChange: React.ComponentProps<"input">["onChange"] = (e) => {
console.log(e);
};
4. Написать свой тип-помощник для определения типа аргументов:
const onChange = (e: EventFor<"input", "onChange">) => {
console.log(e);
};
https://www.totaltypenoscript.com/event-types-in-react-and-typenoscript
Total TypeScript
Event Types in React and TypeScript
Learn how to work with events in React and TypeScript, from onClick to onSubmit.
🔥20👍10
Полное руководство по кукам в Next.js
В Next.js есть несколько способов использования куки в разных ситуациях. Например, в серверных компонентах в App Router можно прочитать куки, но нельзя их изменять. В Pages Router в getServerSideProps можно не только прочитать, но и установить куки, включая флаг HttpOnly.
В статье написаны разные способы чтения и изменения куки в Next.js. Пример изменения куки в серверных компонентах с помощью серверных действий:
https://www.propelauth.com/post/cookies-in-next-js
В Next.js есть несколько способов использования куки в разных ситуациях. Например, в серверных компонентах в App Router можно прочитать куки, но нельзя их изменять. В Pages Router в getServerSideProps можно не только прочитать, но и установить куки, включая флаг HttpOnly.
В статье написаны разные способы чтения и изменения куки в Next.js. Пример изменения куки в серверных компонентах с помощью серверных действий:
import {cookies} from "next/headers";
export default function Message() {
async function markAsSeen() {
'use server'
cookies().set("viewedWelcomeMessage", "true");
}
const viewedWelcomeMessage = cookies().has("viewedWelcomeMessage")
if (viewedWelcomeMessage) {
return <div>Welcome back!</div>
}
return <form action={markAsSeen}>
<button type="submit">Mark as seen</button>
</form>
}
https://www.propelauth.com/post/cookies-in-next-js
Propelauth
A Complete Guide To Using Cookies in Next.js
As Next.js has grown in complexity, it means there are a number of different ways to use cookies, each with their own caveats. You can read them in server components in the App Router, but you can’t modify them.
👍9
Почему мы до сих пор используем React HOC
Если вы используете хуки для асинхронного получения данных, например информация о пользователе, то сталкивались с тем, что надо показывать индикатор загрузки информации. Если это надо сделать только в одном компоненте, то можно сделать прямо в нем. Если же это надо сделать в многих местах, то можно воспользоваться HOC, что поможет уменьшить дублирование кода. Пример:
Кроме показа индикатора загрузки, HOC можно использовать и для других сценариев, например, отображать другой компонент, если информация о пользователе не загрузилась.
https://www.propelauth.com/post/why-we-have-both-react-hooks-and-hocs
Если вы используете хуки для асинхронного получения данных, например информация о пользователе, то сталкивались с тем, что надо показывать индикатор загрузки информации. Если это надо сделать только в одном компоненте, то можно сделать прямо в нем. Если же это надо сделать в многих местах, то можно воспользоваться HOC, что поможет уменьшить дублирование кода. Пример:
const withHookWithLoadingState = (InnerComponent) => {
// Промежуточный компонент, который передает hookResponse в InnerComponent
const WrapperComponent = () => {
const hookResponse = useHookWithLoadingState()
if (hookResponse.loading) {
return <Loading />
}
return <InnerComponent hookResponseData={hookResponse.data} />
}
return WrapperComponent
}
// Использование HOC
const Component = withHookWithLoadingState(({hookResponseData}) => {
// Тело компонента
})
Кроме показа индикатора загрузки, HOC можно использовать и для других сценариев, например, отображать другой компонент, если информация о пользователе не загрузилась.
https://www.propelauth.com/post/why-we-have-both-react-hooks-and-hocs
Propelauth
Why we still use React HOCs
If you’ve integrated with our React library, @propelauth/react, you’ve probably seen that we offer a pretty standard React hook useAuthInfo. It returns a few important pieces of information like:
* isLoggedIn - whether or not the user is logged in, as you…
* isLoggedIn - whether or not the user is logged in, as you…
👎6👍5❤1
Самый быстрый способ передать стейт из сервера на клиент
SSR приложения передают стейт на клиент в виде объекта, который парсится на клиенте. Есть несколько способов передать стейт для клиента и важно выбрать наиболее быстрый вариант, т.к. передача больших стейтов на клиент может стать медленным процессом.
В блоге PerfPlanet провели исследование и определили самый быстрый способ передачи стейта на клиенте. Варианты передачи стейта и название способа:
По результат бенчмарков, "Invalid mime type" парсится на 50% быстрее и занимает на 6% меньше памяти браузера. Это связано с тем, что во время парсинга страницы, браузер не парсит и не компилирует код внутри скрипта с типом "mime/invalid". В "Just parse" строка стейта является частью JS кода, поэтому перед выполнением браузер ее парсит и компилирует, что ухудшает производительность.
https://calendar.perfplanet.com/2023/fastest-way-passing-state-javanoscript-revisited/
SSR приложения передают стейт на клиент в виде объекта, который парсится на клиенте. Есть несколько способов передать стейт для клиента и важно выбрать наиболее быстрый вариант, т.к. передача больших стейтов на клиент может стать медленным процессом.
В блоге PerfPlanet провели исследование и определили самый быстрый способ передачи стейта на клиенте. Варианты передачи стейта и название способа:
// "Plain object":
window.__STATE__ = {"foo":"bar"}
// "Invalid mime type":
<noscript type="mime/invalid" id="myState">{"foo":"bar"}</noscript>
window.__STATE__ = JSON.parse(window.myState.innerHTML)
// "Just parse":
window.__STATE__ = JSON.parse("{\"foo\":\"bar\"}")
По результат бенчмарков, "Invalid mime type" парсится на 50% быстрее и занимает на 6% меньше памяти браузера. Это связано с тем, что во время парсинга страницы, браузер не парсит и не компилирует код внутри скрипта с типом "mime/invalid". В "Just parse" строка стейта является частью JS кода, поэтому перед выполнением браузер ее парсит и компилирует, что ухудшает производительность.
https://calendar.perfplanet.com/2023/fastest-way-passing-state-javanoscript-revisited/
Web Performance Calendar
Fastest Way of Passing State to JavaScript, Re-visited
Revisiting a study of the fastest way to parse large chunks of JSON such as those often used in SPA-type applications to set up state.
🔥14👍7
Полиморфизм в React: 2 паттерна, о которых нужно знать
Если вы делаете UI компоненты, то должны знать о полиморфизме. Пример полиморфизма в React - это когда кнопка может выступать в качестве ссылки. При правильном использовании, полиморфизм избавляет от необходимости поддерживать множество вариантов и дает большую гибкость компонента.
В статье рассказывают про два наиболее известных способа реализации полиморфизма в React: через проп as и asChild. Пример реализации asChild на TypeScript с помощью библиотеки radix:
https://www.bekk.christmas/post/2023/1/polymorphism-in-react
Если вы делаете UI компоненты, то должны знать о полиморфизме. Пример полиморфизма в React - это когда кнопка может выступать в качестве ссылки. При правильном использовании, полиморфизм избавляет от необходимости поддерживать множество вариантов и дает большую гибкость компонента.
В статье рассказывают про два наиболее известных способа реализации полиморфизма в React: через проп as и asChild. Пример реализации asChild на TypeScript с помощью библиотеки radix:
import { Slot } from "@radix-ui/react-slot";
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
asChild?: boolean;
}
export function Button({ asChild, ...props }: ButtonProps) {
const Tag = asChild ? Slot : "button";
return <Tag className="button" {...props} />;
}
https://www.bekk.christmas/post/2023/1/polymorphism-in-react
Bekk Christmas
Polymorphism in React: 2 patterns you must know
Whether you're making a library or making React components for your own sake, there's one technique you must know: polymorphism. It's when one thing can be multiple shapes, as when a button can act as a link. Used correct, it can save you from maintaining…
👍11🔥5
Как JSX преобразуется в HTML
В блоге Дэна Абрамова появилась статья с названием «Цепная реакция». В ней описано как JSX преобразуется в HTML код, понятный для браузера. В статье приводится пример упрощенной функции, которая трансформирует JSX:
Пример как эта функция работает:
С помощью функции translateForBrowser можно преобразовать JSX код в HTML строку или в набор инструкций для обновления существующего DOM.
https://overreacted.io/a-chain-reaction/
В блоге Дэна Абрамова появилась статья с названием «Цепная реакция». В ней описано как JSX преобразуется в HTML код, понятный для браузера. В статье приводится пример упрощенной функции, которая трансформирует JSX:
function translateForBrowser(originalJSX) {
const { type, props } = originalJSX;
if (typeof type === 'function') {
const returnedJSX = type(props);
return translateForBrowser(returnedJSX);
} else if (typeof type === 'string') {
return {
type,
props: {
...props,
children: translateForBrowser(props.children)
}
};
}
}
Пример как эта функция работает:
function Greeting({ person }) {
return (
<p className="text">
Hello, <i>{person.firstName}</i>!
</p>
);
}
const originalJSX = <Greeting person={alice} />;
console.log(originalJSX.type); // Greeting
console.log(originalJSX.props); // { firstName: 'Alice', birthYear: 1970 }
const browserJSX = translateForBrowser(originalJSX);
console.log(browserJSX.type); // 'p'
console.log(browserJSX.props); // { className: 'text', children: ['Hello', { type: 'i', props: { children: 'Alice' }, '!'] }
С помощью функции translateForBrowser можно преобразовать JSX код в HTML строку или в набор инструкций для обновления существующего DOM.
https://overreacted.io/a-chain-reaction/
overreacted.io
A Chain Reaction — overreacted
The limits of my language mean the limits of my world.
👍26🔥3👎2
Хитрости React: быстро, удобно и весело
Автор React роутера Wouter поделился хитростями для React, которые улучшают производительность и уменьшают размер бандла. Автор рекомендует их применять, когда библиотека уже выпущена и начинается этап доработок, в ходе которых можно улучшить метрики производительности. О чем пишет автор:
- Используйте React.cloneElement для композиций. Если нет доступа к элементу, но нужно изменить его пропсы, то можно использовать клонирование элемента:
Также с помощью клонирования можно установить свой реф и получить доступ к DOM элементу.
- Улучшайте производительность. По умолчанию компоненты в React не являются «чистыми». Используйте React.memo, чтобы предотвратить лишние ре-рендеры, и useMemo, useCallback для мемоизации пропсов.
- Используйте useSyncExternalStore для подписки на изменения внешнего состояния. Из-за конкурентного рендеринга разные части приложения могут не соответствовать глобальному состоянию. Если вы используете внешнее состояние напрямую, то могут возникнуть нежелательные сбои, поэтому используйте хук useSyncExternalStore. Например, в Wouter хуки useBrowserLocation, useHashLocation, useSearch используют useSyncExternalStore.
https://molefrog.com/notes/react-tricks
Автор React роутера Wouter поделился хитростями для React, которые улучшают производительность и уменьшают размер бандла. Автор рекомендует их применять, когда библиотека уже выпущена и начинается этап доработок, в ходе которых можно улучшить метрики производительности. О чем пишет автор:
- Используйте React.cloneElement для композиций. Если нет доступа к элементу, но нужно изменить его пропсы, то можно использовать клонирование элемента:
cloneElement(<img />, { src: "keyboard-cat.webp" })
// <img src="keyboard-cat.webp" />
Также с помощью клонирования можно установить свой реф и получить доступ к DOM элементу.
- Улучшайте производительность. По умолчанию компоненты в React не являются «чистыми». Используйте React.memo, чтобы предотвратить лишние ре-рендеры, и useMemo, useCallback для мемоизации пропсов.
- Используйте useSyncExternalStore для подписки на изменения внешнего состояния. Из-за конкурентного рендеринга разные части приложения могут не соответствовать глобальному состоянию. Если вы используете внешнее состояние напрямую, то могут возникнуть нежелательные сбои, поэтому используйте хук useSyncExternalStore. Например, в Wouter хуки useBrowserLocation, useHashLocation, useSearch используют useSyncExternalStore.
https://molefrog.com/notes/react-tricks
Molefrog
React Tricks: Fast, Fit and Fun
How to make your React app or library faster and smaller. Tips and tricks: `useEvent`, `useSyncExternalStore`, stable object references, readonly `useState`.
👍11❤1
Оптимизация производительности в React
Обзорная статья на Sitepoint, в которой рассказали, как делать оптимизацию производительности в React. В ней описано какие инструменты использовать для поиска узких мест, как использовать мемоизацию, ленивую загрузку, виртуализацию и другие способы оптимизации. Кратко о чем пишет автор:
- Узкое место в приложении может замедлить работу всей системы, и, чтобы найти его, есть несколько инструментов: React Developer Tools, Chrome DevTools -> вкладка Performance, React Profiler API.
- Один из вариантов ускорения работы медленного компонента – мемоизация React.memo.
- Обращайте внимание на локальный стейт компонента. Минимизация изменения стейта предотвращает от лишних ре-рендеров. Убедитесь, что обновление стейта происходит только по необходимости.
- Используйте ленивую загрузку и разделение кода. Это ускорит загрузку сайта. Например, если только на одной странице используется большая библиотека, то не надо грузить код библиотеки в общий бандл, можно разделить код по страницам.
- Используйте виртуализацию для больших списков. Это сократит потребление памяти браузера.
https://www.sitepoint.com/react-performance-optimization/
Обзорная статья на Sitepoint, в которой рассказали, как делать оптимизацию производительности в React. В ней описано какие инструменты использовать для поиска узких мест, как использовать мемоизацию, ленивую загрузку, виртуализацию и другие способы оптимизации. Кратко о чем пишет автор:
- Узкое место в приложении может замедлить работу всей системы, и, чтобы найти его, есть несколько инструментов: React Developer Tools, Chrome DevTools -> вкладка Performance, React Profiler API.
- Один из вариантов ускорения работы медленного компонента – мемоизация React.memo.
- Обращайте внимание на локальный стейт компонента. Минимизация изменения стейта предотвращает от лишних ре-рендеров. Убедитесь, что обновление стейта происходит только по необходимости.
- Используйте ленивую загрузку и разделение кода. Это ускорит загрузку сайта. Например, если только на одной странице используется большая библиотека, то не надо грузить код библиотеки в общий бандл, можно разделить код по страницам.
- Используйте виртуализацию для больших списков. Это сократит потребление памяти браузера.
https://www.sitepoint.com/react-performance-optimization/
SitePoint
React Performance Optimization
Learn how to identify bottlenecks and use memoization, lazy loading, virtualization, and more to create highly performant React applications.
👍9
Легкий способ исправить ошибки гидратации – React Hydration Overlay
В блоге Builder.io рассказали про библиотеку React Hydration Overlay, которая помогает исправить ошибки гидратации. Это библиотека пока что работает только для Next.js.
Сейчас в Next.js при возникновении ошибки гидратации появляется не очень информативное сообщение, в котором не совсем понятно в каком месте не совпадают элементы на клиенте и в SSR. Библиотека более точно показывает в каком месте есть несовпадение.
Работает она следующим образом: скрипт сохраняет HTML полученный с сервера и когда происходит ошибка гидратации сохраняет текущий HTML. После чего происходит сравнение двух HTML строк и поиск отличий. То что отличается от серверного HTML и есть причина ошибки гидратации, которую надо исправить.
https://www.builder.io/blog/announcing-react-hydration-overlay
В блоге Builder.io рассказали про библиотеку React Hydration Overlay, которая помогает исправить ошибки гидратации. Это библиотека пока что работает только для Next.js.
Сейчас в Next.js при возникновении ошибки гидратации появляется не очень информативное сообщение, в котором не совсем понятно в каком месте не совпадают элементы на клиенте и в SSR. Библиотека более точно показывает в каком месте есть несовпадение.
Работает она следующим образом: скрипт сохраняет HTML полученный с сервера и когда происходит ошибка гидратации сохраняет текущий HTML. После чего происходит сравнение двух HTML строк и поиск отличий. То что отличается от серверного HTML и есть причина ошибки гидратации, которую надо исправить.
https://www.builder.io/blog/announcing-react-hydration-overlay
👍12🔥7❤2
Релиз React Aria 1.0
React Aria – библиотека компонентов от Adobe, более 40 компонентов, поддерживающих доступность и интернационализацию, а также кастомизацию стилей.
React Aria поддерживает доступный drag and drop, управление клавиатурой, встроенная валидация формы, изменение размеров колонок таблиц и многое другое.
Компоненты оптимизированы для взаимодействия на разных устройствах и поддерживают мышь, сенсорный экран, клавиатура и screen reader.
Компоненты в React Aria декомпозированы, поэтому можно самому настраивать нужный вид и компоновать их:
https://react-spectrum.adobe.com/react-aria/index.html
React Aria – библиотека компонентов от Adobe, более 40 компонентов, поддерживающих доступность и интернационализацию, а также кастомизацию стилей.
React Aria поддерживает доступный drag and drop, управление клавиатурой, встроенная валидация формы, изменение размеров колонок таблиц и многое другое.
Компоненты оптимизированы для взаимодействия на разных устройствах и поддерживают мышь, сенсорный экран, клавиатура и screen reader.
Компоненты в React Aria декомпозированы, поэтому можно самому настраивать нужный вид и компоновать их:
<ComboBox>
<Label>Permissions</Label>
<Group>
<Input />
<Button>▼</Button>
</Group>
<Popover>
<ListBox>
<ListBoxItem>Read Only</ListBoxItem>
<ListBoxItem>Edit</ListBoxItem>
<ListBoxItem>Admin</ListBoxItem>
</ListBox>
</Popover>
</ComboBox>
https://react-spectrum.adobe.com/react-aria/index.html
Adobe
React Aria
Craft world-class accessible components with custom styles.
👍20
Конкурентный React, внешний стор и «разрыв» стейта
В блоге InterBolt рассказали, что такое «разрыв» стейта, как это связано с библиотеками управления глобальным стейтом, используемых в режиме конкурентного рендеринга, и какие есть решения и компромиссы.
«Разрыв» стейта возникает, когда два и более компонентов рендерят разный UI от одного и того же источника данных.
Библиотеки, использующие внешний стор, дают пользователю точечную реактивность для предотвращения лишних ререндеров, но не поддерживают конкурентный рендеринг, чтобы избежать «разрыва» состояния.
Библиотеки, использующие стор в виде контекста, поддерживают конкурентный рендеринг, но не дают точечную реактивность из коробки – приходится использовать мемоизацию.
И есть библиотеки, которые поддерживают и конкурентный рендеринг и точечную реактивность, например Jotai, но страдают от краткосрочных «разрывов» стейта пока происходит реконсиляция.
Для решения проблемы «разрыва» стейта при доступе к внешнему стору можно использовать хук useSyncExternalStore. С помощью него можно получить доступ к внешнему стейту и использовать конкурентный рендеринг.
https://interbolt.org/blog/react-ui-tearing/
В блоге InterBolt рассказали, что такое «разрыв» стейта, как это связано с библиотеками управления глобальным стейтом, используемых в режиме конкурентного рендеринга, и какие есть решения и компромиссы.
«Разрыв» стейта возникает, когда два и более компонентов рендерят разный UI от одного и того же источника данных.
Библиотеки, использующие внешний стор, дают пользователю точечную реактивность для предотвращения лишних ререндеров, но не поддерживают конкурентный рендеринг, чтобы избежать «разрыва» состояния.
Библиотеки, использующие стор в виде контекста, поддерживают конкурентный рендеринг, но не дают точечную реактивность из коробки – приходится использовать мемоизацию.
И есть библиотеки, которые поддерживают и конкурентный рендеринг и точечную реактивность, например Jotai, но страдают от краткосрочных «разрывов» стейта пока происходит реконсиляция.
Для решения проблемы «разрыва» стейта при доступе к внешнему стору можно использовать хук useSyncExternalStore. С помощью него можно получить доступ к внешнему стейту и использовать конкурентный рендеринг.
https://interbolt.org/blog/react-ui-tearing/
InterBolt
Concurrent React, External Stores, and Tearing
React 18 exposes an API to do concurrent rendering. But when used alongside external stores, this concurrency introduces a theoretical issue called tearing. Let's explore what that means and how to avoid it.
👍9🔥2
Puck: self-hosted drag-and-drop редактор лендинга на React
Puck - это модульный, с открытым исходным кодом, визуальный редактор лендинга на React. Аналог Tilda, компоненты которого можно настроить под себя и опубликовать на своем сервере без вендор-лока. Т.к. это React, то можно использовать Next.js для рендера на сервере.
Можно самостоятельно настроить вид компонентов, список полей и их тип. Пример:
Puck дает два основных компонента: редактор и рендер страницы. Редактор принимает конфиг компонентов, данные по умолчанию и колбек для сохранения изменений, пример:
Для рендера страницы надо передать конфиг компонентов и данные:
https://puckeditor.com/
Puck - это модульный, с открытым исходным кодом, визуальный редактор лендинга на React. Аналог Tilda, компоненты которого можно настроить под себя и опубликовать на своем сервере без вендор-лока. Т.к. это React, то можно использовать Next.js для рендера на сервере.
Можно самостоятельно настроить вид компонентов, список полей и их тип. Пример:
const config = {
components: {
HeadingBlock: {
fields: {
noscript: {
type: "text",
},
},
defaultProps: {
noscript: "Hello, world",
},
render: ({ noscript }) => {
return <h1>{noscript}</h1>;
},
},
},
};
Puck дает два основных компонента: редактор и рендер страницы. Редактор принимает конфиг компонентов, данные по умолчанию и колбек для сохранения изменений, пример:
// Данные по умолчанию
const initialData = {
content: [],
root: {},
};
// Измененные данные в виде JSON
const save = (data) => {};
// Рендер редактора
export function Editor() {
return <Puck config={config} data={initialData} onPublish={save} />;
}
Для рендера страницы надо передать конфиг компонентов и данные:
export function Page() {
return <Render config={config} data={data} />;
}
https://puckeditor.com/
Puck - Agentic visual editor for React
Puck empowers developers to build amazing visual editing experiences into their own React applications, powering the next generation of content tools.
👍15🔥2
Серверные компоненты React: хороший, плохой и уродливый
Обзор серверных компонентов React: что кажется удобным и хорошим, а что не очень.
С помощью серверных компонентов можно получать данные и рендерить UI на сервере:
Еще удобно сразу обрабатывать данные формы:
Из-за прогрессивного улучшения эта форма будет работать даже если JS будет выключен. Однако, если потребуется дополнительно показывать состояние загрузки формы или ошибки отправки формы, то код усложнится: придется добавить хуки useFormState и useFormStatus. Пример:
Такой подход уже не так удобен, т.к. близкий код уже не располагается рядом.
Еще одним минусом серверных компонент стал манки-патчинг fetch API в Next.js:
- по умолчанию все запросы кэшируются.
- нет доступа к объекту Request вне мидлвара.
Также, с серверными компонентами вырос размер JS бандла. В Next.js 12 базовый бандл весил ~70КБ, сейчас Next.js 14 весит 85-90КБ в сжатом виде. Не в сжатом виде, бандл весит 300КБ.
https://www.mayank.co/blog/react-server-components
Обзор серверных компонентов React: что кажется удобным и хорошим, а что не очень.
С помощью серверных компонентов можно получать данные и рендерить UI на сервере:
export default async function Page() {
const stuff = await fetch(/* … */);
return <div>{stuff}</div>;
}
Еще удобно сразу обрабатывать данные формы:
<form
action={async (formData) => {
"use server";
const email = formData.get("email");
await db.emails.insert({ email });
}}
>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" />
<button>Send me spam</button>
</form>
Из-за прогрессивного улучшения эта форма будет работать даже если JS будет выключен. Однако, если потребуется дополнительно показывать состояние загрузки формы или ошибки отправки формы, то код усложнится: придется добавить хуки useFormState и useFormStatus. Пример:
"use client";
const [formState, formAction] = useFormState(saveEmailAction);
<form action={formAction}>
<!-- snip -->
</form>
Такой подход уже не так удобен, т.к. близкий код уже не располагается рядом.
Еще одним минусом серверных компонент стал манки-патчинг fetch API в Next.js:
- по умолчанию все запросы кэшируются.
- нет доступа к объекту Request вне мидлвара.
Также, с серверными компонентами вырос размер JS бандла. В Next.js 12 базовый бандл весил ~70КБ, сейчас Next.js 14 весит 85-90КБ в сжатом виде. Не в сжатом виде, бандл весит 300КБ.
https://www.mayank.co/blog/react-server-components
mayank.co
React Server Components: the Good, the Bad, and the Ugly
Evaluating Next.js's implementation of React's new server features.
👍8❤2
Углубленный взгляд на ReactDOM.flushSync и для чего он нужен
flushSync – простое API чтобы заставить React выполнить всю ожидающую работу и синхронно обновить DOM. Ожидающая работа – изменение стейта в компонентах.
В основном, flushSync используют для отмены батчинга изменения стейта в колбеках, пример:
При вызове колбека handleClick, каждое выполнение flushSync заставить React перерендерить компонент и синхронно обновить DOM, вместо одного раза в конце колбека.
Также, flushSync используют, когда после изменения стейта нужно сразу обратиться в DOM, например для измерения DOM узла. Еще пример использования – скролл к элементу сразу после обновления стейта.
https://julesblom.com/writing/flushsync
flushSync – простое API чтобы заставить React выполнить всю ожидающую работу и синхронно обновить DOM. Ожидающая работа – изменение стейта в компонентах.
В основном, flushSync используют для отмены батчинга изменения стейта в колбеках, пример:
function Demo() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);
function handleClick() {
flushSync(() => {
setCount((c) => c + 1);
});
flushSync(() => {
setFlag((f) => !f);
});
}
{/* --snip-- */}
}При вызове колбека handleClick, каждое выполнение flushSync заставить React перерендерить компонент и синхронно обновить DOM, вместо одного раза в конце колбека.
Также, flushSync используют, когда после изменения стейта нужно сразу обратиться в DOM, например для измерения DOM узла. Еще пример использования – скролл к элементу сразу после обновления стейта.
https://julesblom.com/writing/flushsync
JulesBlom.com
More Than You Need to Know About ReactDOM.flushSync | JulesBlom.com
An in-depth look at what ReactDOM.flushSync does and what it’s good for.
👍20❤2
Селектор контекста через React.use и React.useMemo
Использование контекста в качестве глобального стейта приложения ухудшает производительность приложения, т.к. изменение значения контекста приведет к ререндеру всех компонентов, которые использует данный контекст.
Для оптимизации контекста можно использовать библиотеку use-context-selector. Библиотека предоставляет селектор для контекста и ограничивает ререндеры компонента только на изменение выбранных в селекторе значений. Пример:
Если библиотека use-context-selector не подходит, то скоро появится возможность оптимизировать контекст через useMemo и функцию use. Пример:
На данный момент функция use доступна в canary версии React. Как видно по примеру, в useMemo не надо ничего передавать в массив зависимостей, React сам поймет когда нужно произвести ререндер когда foo изменится.
https://interbolt.org/blog/react-use-selector-optimization/
Использование контекста в качестве глобального стейта приложения ухудшает производительность приложения, т.к. изменение значения контекста приведет к ререндеру всех компонентов, которые использует данный контекст.
Для оптимизации контекста можно использовать библиотеку use-context-selector. Библиотека предоставляет селектор для контекста и ограничивает ререндеры компонента только на изменение выбранных в селекторе значений. Пример:
import { createContext, useContextSelector } from 'use-context-selector';
const context = createContext(null);
const Counter1 = () => {
const count1 = useContextSelector(context, v => v[0].count1);
const setState = useContextSelector(context, v => v[1]);
const increment = () => setState(s => ({
...s,
count1: s.count1 + 1,
}));
{/* --snip-- */}
};
const StateProvider = ({ children }) => (
<context.Provider value={useState({ count1: 0, count2: 0 })}>
{children}
</context.Provider>
);
Если библиотека use-context-selector не подходит, то скоро появится возможность оптимизировать контекст через useMemo и функцию use. Пример:
import { createContext, useMemo, use } from 'react';
const MyContext = createContext({
foo: '',
bar: 0,
});
const useFoo = () => {
return useMemo(() => {
const { foo } = use(MyContext);
return foo;
}, []);
};
На данный момент функция use доступна в canary версии React. Как видно по примеру, в useMemo не надо ничего передавать в массив зависимостей, React сам поймет когда нужно произвести ререндер когда foo изменится.
https://interbolt.org/blog/react-use-selector-optimization/
InterBolt
The future of React.use and React.useMemo - a powerful alternative to context selectors
use-context-selector is my preferred way to prevent re-renders when using React contexts. But some future React updates provide a better alternative using only React.use and React.useMemo. Follow along as I learn a powerful lesson in my unsuccessfully attempt…
👍17👎3
Продвинутые фичи Next.js
Подборка менее известных фич в Next.js, о которых полезно знать. Краткий список:
- Автоматическая типизация <Link />:
В настройках можно включить типизацию ссылок в компоненте <Link />. Сгенерится d.ts файл, в котором будут типы созданных роутов, и можно будет безопасно использовать href в <Link />.
- Встроенные модули для сторонних скриптов Google. В Next.js есть встроенные компоненты для распространенных скриптов Google в
- Простая генерация sitemap.xml. Нужно создать страницу sitemap.ts и вернуть в нем массив метаданных о страницах:
- Если в src расположить файл instrumentation.ts, то Next.js будет запускать экспортируемую функцию register при запуске сервера. Может пригодиться, если надо запускать внешний код при запуске приложения.
- generateViewport (app router) для генерации мета-тегов в серверных компонентах:
https://codedrivendevelopment.com/posts/rarely-known-nextjs-features
Подборка менее известных фич в Next.js, о которых полезно знать. Краткий список:
- Автоматическая типизация <Link />:
const nextConfig = {
experimental: {
typedRoutes: true,
},
}
В настройках можно включить типизацию ссылок в компоненте <Link />. Сгенерится d.ts файл, в котором будут типы созданных роутов, и можно будет безопасно использовать href в <Link />.
- Встроенные модули для сторонних скриптов Google. В Next.js есть встроенные компоненты для распространенных скриптов Google в
@next/third-parties/google: GoogleTagManager, GoogleAnalytics, GoogleMapsEmbed, YouTubeEmbed.- Простая генерация sitemap.xml. Нужно создать страницу sitemap.ts и вернуть в нем массив метаданных о страницах:
import { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://acme.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
}
]
}
- Если в src расположить файл instrumentation.ts, то Next.js будет запускать экспортируемую функцию register при запуске сервера. Может пригодиться, если надо запускать внешний код при запуске приложения.
- generateViewport (app router) для генерации мета-тегов в серверных компонентах:
export function generateViewport({ params }) {
return {
themeColor: 'black',
}
/*
<meta name="theme-color" content="black" />
*/
}
https://codedrivendevelopment.com/posts/rarely-known-nextjs-features
👍14👎1
Настройка отчетов о «мягкой» навигации в React
Отчет о «мягкой» навигации – это экспериментальная фича Google Web Vitals. С ее помощью можно измерять Core Web Vitals и другие метрики для динамических URL в SPA. «Мягкая» навигация происходит при изменении URL с помощью JavaScript, что типично для SPA приложений.
Что нужно сделать, чтобы подключить отчеты в приложении:
1) Включить экспериментальную фичу
2) Установить web-vitals с «мягкой» навигацией
3) Создать функцию для отчетов в src/reportWebVitals.js и вызвать ее в src/index.js:
https://www.debugbear.com/blog/soft-navigations-react-tutorial
Отчет о «мягкой» навигации – это экспериментальная фича Google Web Vitals. С ее помощью можно измерять Core Web Vitals и другие метрики для динамических URL в SPA. «Мягкая» навигация происходит при изменении URL с помощью JavaScript, что типично для SPA приложений.
Что нужно сделать, чтобы подключить отчеты в приложении:
1) Включить экспериментальную фичу
chrome://flags/#enable-experimental-web-platform-features. 2) Установить web-vitals с «мягкой» навигацией
npm i web-vitals@3.5.0-soft-navs-10/3) Создать функцию для отчетов в src/reportWebVitals.js и вызвать ее в src/index.js:
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ onTTFB, onFCP, onLCP, onCLS, onFID, onINP }) => {
onTTFB(onPerfEntry, {reportSoftNavs: true});
onFCP(onPerfEntry, {reportSoftNavs: true});
onLCP(onPerfEntry, {reportSoftNavs: true});
onCLS(onPerfEntry, {reportSoftNavs: true});
onFID(onPerfEntry, {reportSoftNavs: true});
onINP(onPerfEntry, {reportSoftNavs: true});
});
}
};
export default reportWebVitals;
https://www.debugbear.com/blog/soft-navigations-react-tutorial
Debugbear
How to Set Up Soft Navigation Reporting in a React App | DebugBear
In this tutorial, we look into how you can report Core Web Vitals for soft navigations from a React application to the Chrome DevTools Console.
👍5🔥1