Вам все еще нужен Framer Motion?
Мэтт Перри, автор библиотеки Framer Motion, в своем блоге поделился о новых фичах в CSS. Благодаря этим фичам возможно вам больше не понадобится библиотека Framer Motion.
Автор сравнил популярные виды анимаций в Framer Motion и как их можно реализовать через CSS:
- Вводная анимация. Чтобы сделать анимацию при появлении элемента в документе или при загрузке страницы, можно использовать CSS правило
- Независимая трансформация. Появятся свойства translate, scale и rotate для независимой трансформации элемента.
- Spring анимация. Для создания spring анимации можно использовать CSS функцию linear(). Функция принимает серию точек и линейно интерполирует между ними. Чтобы не писать серию точек вручную, используйте онлайн генераторы функции linear().
- Анимация по скроллу. В CSS появились две timeline анимации scroll() и view(). Их можно назначить в свойство animation-timeline для управления анимацией посредством скролла, а не времени:
- Бонус, анимация высоты. В CSS нет возможности анимировать в/из auto, но CSS5 функция calc-size позволит это сделать:
https://motion.dev/blog/do-you-still-need-framer-motion
Мэтт Перри, автор библиотеки Framer Motion, в своем блоге поделился о новых фичах в CSS. Благодаря этим фичам возможно вам больше не понадобится библиотека Framer Motion.
Автор сравнил популярные виды анимаций в Framer Motion и как их можно реализовать через CSS:
- Вводная анимация. Чтобы сделать анимацию при появлении элемента в документе или при загрузке страницы, можно использовать CSS правило
@starting-style. Это правило определяет первоначальные стили, из которых элемент будет анимироваться:
#my-element {
opacity: 1;
transition: opacity 0.5s;
@starting-style {
opacity: 0;
}
}
- Независимая трансформация. Появятся свойства translate, scale и rotate для независимой трансформации элемента.
button {
translate: 0px 0px;
transition:
translate 0.2s ease-out,
scale 0.5s ease-in-out;
}
button:hover {
scale: 1.2;
}
- Spring анимация. Для создания spring анимации можно использовать CSS функцию linear(). Функция принимает серию точек и линейно интерполирует между ними. Чтобы не писать серию точек вручную, используйте онлайн генераторы функции linear().
- Анимация по скроллу. В CSS появились две timeline анимации scroll() и view(). Их можно назначить в свойство animation-timeline для управления анимацией посредством скролла, а не времени:
div {
animation-name: fadeAnimation;
animation-timeline: scroll();
}
- Бонус, анимация высоты. В CSS нет возможности анимировать в/из auto, но CSS5 функция calc-size позволит это сделать:
li {
height: 0px;
transition: height 0.3s ease-out;
.open {
height: calc-size(auto);
}
}
https://motion.dev/blog/do-you-still-need-framer-motion
motion.dev
Do you still need Framer Motion? - Motion Blog
In the five years since Framer Motion was released, CSS animation APIs have come a long way. Do you still need to use Framer Motion?
👍27🔥9
This media is not supported in your browser
VIEW IN TELEGRAM
Создание переиспользуемой SubmitButton
В React 19 появился хук useFormStatus для получения статуса формы. С его помощью можно создать переиспользуемую кнопку действия, которая будет сама переходить в состояние прогресса и дизейблиться. Пример компонента кнопки:
В своей статье автор привел пример использования этой кнопки в React 19 с серверными функциями. Такой пример будет работать, даже когда JavaScript выключен или когда кнопка еще не гидратировала:
https://aurorascharff.no/posts/creating-a-reusable-submitbutton-with-useformstatus
В React 19 появился хук useFormStatus для получения статуса формы. С его помощью можно создать переиспользуемую кнопку действия, которая будет сама переходить в состояние прогресса и дизейблиться. Пример компонента кнопки:
export default function SubmitButton({
children,
loading,
disabled,
...otherProps
}: Props & React.HTMLProps<HTMLButtonElement>) {
const { pending } = useFormStatus();
const isSubmitting = pending || loading;
return (
<Button
{...otherProps}
disabled={isSubmitting || disabled}
type="submit"
>
{isSubmitting ? (
// progress
) : (
children
)}
</Button>
);
}
В своей статье автор привел пример использования этой кнопки в React 19 с серверными функциями. Такой пример будет работать, даже когда JavaScript выключен или когда кнопка еще не гидратировала:
export default function Component({ contactId }: { contactId: string }) {
return (
<form action={deleteRecord}>
<input type="hidden" name="contactId" value={contactId} />
<SubmitButton>Delete</SubmitButton>
</form>
);
}
https://aurorascharff.no/posts/creating-a-reusable-submitbutton-with-useformstatus
👍10❤1🔥1
Чеклист безопасности в Next.js
Описание подходов, которые используются в SDK Arcjet для повышения безопасности Next.js. Хорошая безопасность обеспечивается за счет многоуровневого подхода - ничто не может быть защищено на 100%, но есть несколько простых вещей, которые каждый может сделать для снижения риска.
- Контроль зависимостей. Поддерживайте ваши зависимости в актуальном состоянии. Используйте package-lock.json, чтобы гарантированно устанавливать одинаковую версию. Периодически проводите аудит безопасности ваших зависимостей.
- Валидация и санитизация данных. Экранирование и санитизация React помогает предотвратить XSS атаки, но это не покрывает все потребности. Используйте схему для валидации данных, пришедших от пользователя. Например, для создания схемы можно использовать Zod:
- Переменные окружения. Next.js автоматически загружает переменные окружения на сервер. Если хотите, чтобы переменная стала доступна на клиенте, то у нее должен быть префикс NEXT_PUBLIC_. Однако будьте осторожны: любое значение, которое вы раскрываете таким образом, становится публичным.
- Избегайте раскрытия кода. Чтобы избежать передачи серверного кода на клиент, используйте библиотеку server-only. Импортируйте ее в начале файла серверного компонента. Если клиентский компонент попытается использовать серверный компонент, то сборка упадет.
- Заголовки безопасности. При отдаче страницы сервером выставляйте заголовки безопасности. Они могут предотвратить распространенные веб-атаки. Основные заголовки, которые следует использовать: Content Security Policy (CSP), Strict-Transport-Security (HSTS), X-Content-Type-Options, Permissions-Policy, Referrer-Policy.
https://blog.arcjet.com/next-js-security-checklist/
Описание подходов, которые используются в SDK Arcjet для повышения безопасности Next.js. Хорошая безопасность обеспечивается за счет многоуровневого подхода - ничто не может быть защищено на 100%, но есть несколько простых вещей, которые каждый может сделать для снижения риска.
- Контроль зависимостей. Поддерживайте ваши зависимости в актуальном состоянии. Используйте package-lock.json, чтобы гарантированно устанавливать одинаковую версию. Периодически проводите аудит безопасности ваших зависимостей.
- Валидация и санитизация данных. Экранирование и санитизация React помогает предотвратить XSS атаки, но это не покрывает все потребности. Используйте схему для валидации данных, пришедших от пользователя. Например, для создания схемы можно использовать Zod:
export const insertTeamSchema = createInsertSchema(teams, {
name: (schema) =>
schema.name
.min(2, { message: "Must be 2 or more characters." })
.max(100, { message: "Must be 100 or fewer characters." })
.trim(),
});
- Переменные окружения. Next.js автоматически загружает переменные окружения на сервер. Если хотите, чтобы переменная стала доступна на клиенте, то у нее должен быть префикс NEXT_PUBLIC_. Однако будьте осторожны: любое значение, которое вы раскрываете таким образом, становится публичным.
- Избегайте раскрытия кода. Чтобы избежать передачи серверного кода на клиент, используйте библиотеку server-only. Импортируйте ее в начале файла серверного компонента. Если клиентский компонент попытается использовать серверный компонент, то сборка упадет.
- Заголовки безопасности. При отдаче страницы сервером выставляйте заголовки безопасности. Они могут предотвратить распространенные веб-атаки. Основные заголовки, которые следует использовать: Content Security Policy (CSP), Strict-Transport-Security (HSTS), X-Content-Type-Options, Permissions-Policy, Referrer-Policy.
https://blog.arcjet.com/next-js-security-checklist/
Arcjet blog
Next.js security checklist
A security checklist of 7 things to improve the security of your Next.js applications. Dependencies, data validation & sanitization, environment variables, code exposure, security headers, centralizing functions, getting help from your editor.
👍17👎2
Suspense и React 19 RC
В React 19 RC появилось спорное изменение в работе Suspense.
В 18 версии внутри Suspense рендерелись все компоненты и отображался fallback, пока внутри компонентов происходила загрузка данных. После окончания загрузки данных во всех компонентах вместо fallback отображались сами компоненты.
В React 19 RC изменилось поведение Suspense. Если компонент приостановится, то его соседние компоненты не будут отрендерены до тех пор, пока компонент не закончит загрузку данных. Это привело к тому, что вместо параллельной загрузки данных во всех компонентах, он начал делать последовательную загрузку данных, создавая «водопад» запросов.
У этого решения есть и положительный эффект: есть возможность сразу показывать fallback, избегая лишнего рендера.
Для решения проблемы «водопада» запросов нужно делать предварительные запросы на уровне роута:
В этом случае, когда React начнет рендерить дочерние компоненты Suspense, не имеет значения, будет ли он рендерить второй компонент RepoData или нет, потому что данные для него уже были запрошены.
Скорее всего поведение Suspense еще изменится перед релизом React 19, т.к. не всех устраивает текущее поведение. Например, библиотека react-three-fiber основана на асинхронной работе и полагается на то, как работает Suspense в React 18.
https://tkdodo.eu/blog/react-19-and-suspense-a-drama-in-3-acts
В React 19 RC появилось спорное изменение в работе Suspense.
В 18 версии внутри Suspense рендерелись все компоненты и отображался fallback, пока внутри компонентов происходила загрузка данных. После окончания загрузки данных во всех компонентах вместо fallback отображались сами компоненты.
export default function App() {
return (
<Suspense fallback={<p>...</p>}>
<Header />
<Navbar />
<main>
<Content />
</main>
<Footer />
</Suspense>
)
}
В React 19 RC изменилось поведение Suspense. Если компонент приостановится, то его соседние компоненты не будут отрендерены до тех пор, пока компонент не закончит загрузку данных. Это привело к тому, что вместо параллельной загрузки данных во всех компонентах, он начал делать последовательную загрузку данных, создавая «водопад» запросов.
У этого решения есть и положительный эффект: есть возможность сразу показывать fallback, избегая лишнего рендера.
Для решения проблемы «водопада» запросов нужно делать предварительные запросы на уровне роута:
export const Route = createFileRoute('/')({
loader: ({ context: { queryClient } }) => {
queryClient.ensureQueryData(repoOptions('tanstack/query'))
queryClient.ensureQueryData(repoOptions('tanstack/table'))
},
component: () => (
<Suspense fallback={<p>...</p>}>
<RepoData name="tanstack/query" />
<RepoData name="tanstack/table" />
</Suspense>
),
})
В этом случае, когда React начнет рендерить дочерние компоненты Suspense, не имеет значения, будет ли он рендерить второй компонент RepoData или нет, потому что данные для него уже были запрошены.
Скорее всего поведение Suspense еще изменится перед релизом React 19, т.к. не всех устраивает текущее поведение. Например, библиотека react-three-fiber основана на асинхронной работе и полагается на то, как работает Suspense в React 18.
https://tkdodo.eu/blog/react-19-and-suspense-a-drama-in-3-acts
tkdodo.eu
React 19 and Suspense - A Drama in 3 Acts
React 19 is a very promising release - but there's something not quite right yet with suspense...
👍11👎5
Как работает React Compiler
Автор объясняет, как работает компилятор React начиная с самых основ (транспилятор, AST, мемоизация), а затем анализирует результат компилятора на конкретном примере:
Функция
У компилятора есть недостаток: он затрудняет дебаг нашего кода, добавляя новый уровень абстракции. Поэтому стоит научиться понимать, как работает React Compiler, чтобы лучше дебажить компилированный им код.
https://tonyalicea.dev/blog/understanding-react-compiler/
Автор объясняет, как работает компилятор React начиная с самых основ (транспилятор, AST, мемоизация), а затем анализирует результат компилятора на конкретном примере:
function List(t0) {
const $ = _c(6);
const { items } = t0;
/* --snip-- */
let t2;
if ($[1] !== items) {
const pItems = processItems(items);
let t3;
if ($[3] === Symbol.for("react.iss.onemo_cache_sentinel")) {
t3 = (item) => <li>{item}</li>;
$[3] = t3;
} else {
t3 = $[3];
}
t2 = pItems.map(t3);
$[1] = items;
$[2] = t2;
} else {
t2 = $[2];
}
/* --snip-- */
}
Функция
_c (сокращенно от cache) создает массив длинной 6. Компилятор React проанализировал функцию Link и понял, что для максимизации производительности надо хранить 6 элементов. Когда функция Link вызовется впервые, то результат каждой операции сохранится в одном из шести элементов массива $.У компилятора есть недостаток: он затрудняет дебаг нашего кода, добавляя новый уровень абстракции. Поэтому стоит научиться понимать, как работает React Compiler, чтобы лучше дебажить компилированный им код.
https://tonyalicea.dev/blog/understanding-react-compiler/
Tony Alicea
Understanding React Compiler | Tony Alicea
Cache all the things. How React Compiler works under-the-hood.
👎15👍6🔥2❤1
Релиз TypeScript 5.5
Вышел релиз новой версии TypeScript. Список основных изменений:
- Предикаты выводимых типов (Inferred Type Predicates). Одно из главных изменений, которое многим упростит написание кода. Теперь TS сам выводит типы для предикатов-функций, сужая (narrowing) возвращаемый тип. Пример:
Это будет работать для метода массива filter. Если раньше TS не определял тип у элемента массива после фильтрации, то теперь будет. Пример:
- Сужение типа для констант. TS теперь может сужать типы для выражений obj[key], где obj и key константы. Пример:
- Поддержка новых ECMAScript методов для Set. Появилась поддержка новых методов для Set: union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom. Пример:
https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/
Вышел релиз новой версии TypeScript. Список основных изменений:
- Предикаты выводимых типов (Inferred Type Predicates). Одно из главных изменений, которое многим упростит написание кода. Теперь TS сам выводит типы для предикатов-функций, сужая (narrowing) возвращаемый тип. Пример:
// function isBirdReal(bird: Bird | undefined): bird is Bird
function isBirdReal(bird: Bird | undefined) {
return bird !== undefined;
}
Это будет работать для метода массива filter. Если раньше TS не определял тип у элемента массива после фильтрации, то теперь будет. Пример:
function makeBirdCalls(countries: string[]) {
// birds: Bird[]
const birds = countries
.map(country => nationalBirds.get(country))
.filter(bird => bird !== undefined);
for (const bird of birds) {
bird.sing(); // ok!
}
}
- Сужение типа для констант. TS теперь может сужать типы для выражений obj[key], где obj и key константы. Пример:
function f1(obj: Record<string, unknown>, key: string) {
if (typeof obj[key] === "string") {
// Сейчас работает, ранее была ошибка
obj[key].toUpperCase();
}
}
- Поддержка новых ECMAScript методов для Set. Появилась поддержка новых методов для Set: union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom. Пример:
let fruits = new Set(["apples", "bananas", "pears", "oranges"]);
let oranges = new Set(["oranges"]);
// Set(4) {'apples', 'bananas', 'pears', 'oranges'}
console.log(fruits.union(oranges));
https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/
Microsoft News
Announcing TypeScript 5.5
Today we’re excited to announce the release of TypeScript 5.5! If you’re not familiar with TypeScript, it’s a language that builds on top of JavaScript by making it possible to declare and describe types. Writing types in our code allows us to explain intent…
👍37👎1
Ленивое обнаружение роутов в React Router
В новой минорной версии React Router 6.24 появилась возможность ленивого обнаружения роутов. С помощью метода unstable_patchRoutesOnMiss можно загружать дополнительные роуты и реализовывать разделение кода для ваших роутов вместо того, чтобы хранить их все в одном монолитном модуле. Эта фича пригодится для приложений с тысячами роутов, которые теперь не надо будет загружать заранее, задерживая гидратацию.
https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6240
В новой минорной версии React Router 6.24 появилась возможность ленивого обнаружения роутов. С помощью метода unstable_patchRoutesOnMiss можно загружать дополнительные роуты и реализовывать разделение кода для ваших роутов вместо того, чтобы хранить их все в одном монолитном модуле. Эта фича пригодится для приложений с тысячами роутов, которые теперь не надо будет загружать заранее, задерживая гидратацию.
const router = createBrowserRouter(
[
{
id: "root",
path: "/",
Component: RootComponent,
},
],
{
async unstable_patchRoutesOnMiss({ path, patch }) {
if (path === "/a") {
// Загрузка `a` роута (`{ path: 'a', Component: A }`)
let route = await getARoute();
// Установка `a` роута как новый дочерний роут у `root`
patch("root", [route]);
}
},
}
);
https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v6240
GitHub
react-router/CHANGELOG.md at main · remix-run/react-router
Declarative routing for React. Contribute to remix-run/react-router development by creating an account on GitHub.
👍13👎2
5 заблуждений о React Server Components
В React 19 представили большое архитектурное изменение: разделение на серверные и клиентские компоненты. Серверные компоненты выполняются только на сервере, и их код не попадает в клиентский бандл. Клиентские компоненты – обычные компоненты React, к которым уже все привыкли. Эта новая архитектура направлена на использование сильных сторон как на сервере, так и на клиенте.
Возможно, некоторые поняли новую концепцию серверных компонентов неправильно и вот некоторые заблуждения о них:
- Предпочитайте только серверные компоненты, а клиентские используйте редко. Это не так, потому что серверные компоненты не подходят для всех сценариев, особенно которые требуют интерактивности. Серверные компоненты – это новый тип компонентов, которые выполняются только на сервере, получают данные и рендерят их на сервере для повышения производительности.
-
- Каждый клиентский компонент должен иметь директиву
- Серверные компоненты не могут быть вложены в клиентские компоненты. Напрямую импортировать серверный компонент в клиентский нельзя. Но использовать серверный компонент внутри клиентского можно через проп, например, children:
https://www.builder.io/blog/nextjs-react-server-components
В React 19 представили большое архитектурное изменение: разделение на серверные и клиентские компоненты. Серверные компоненты выполняются только на сервере, и их код не попадает в клиентский бандл. Клиентские компоненты – обычные компоненты React, к которым уже все привыкли. Эта новая архитектура направлена на использование сильных сторон как на сервере, так и на клиенте.
Возможно, некоторые поняли новую концепцию серверных компонентов неправильно и вот некоторые заблуждения о них:
- Предпочитайте только серверные компоненты, а клиентские используйте редко. Это не так, потому что серверные компоненты не подходят для всех сценариев, особенно которые требуют интерактивности. Серверные компоненты – это новый тип компонентов, которые выполняются только на сервере, получают данные и рендерят их на сервере для повышения производительности.
async function UserProfile({ userId }) {
const response = await db.query('SELECT * FROM users WHERE id = ?', [userId]);
const user = response.user;
return (
<div>
<h1>{user.name}</h1>
<p>Email: {user.email}</p>
</div>
);
}
-
use client помечает клиентские компоненты, а use server помечает серверные компоненты. На самом деле директива use client помечает клиентские компоненты. Но директива use server не нужна, все компоненты по умолчанию являются серверными. - Каждый клиентский компонент должен иметь директиву
use client. На самом деле достаточно объявить директиву use client один раз в корневом файле клиентского компонента. Все компоненты, определенные в этом файле, автоматически станут клиентскими. Сюда входят вложенные компоненты, утилиты и функции внутри файла.- Серверные компоненты не могут быть вложены в клиентские компоненты. Напрямую импортировать серверный компонент в клиентский нельзя. Но использовать серверный компонент внутри клиентского можно через проп, например, children:
// ServerComponent.js
function ServerComponent() {
return <h1>Hello from the server</h1>;
}
// ClientComponent.js
'use client'
function ClientComponent({ children }) {
return (
<div>
<p>This is a client component</p>
{children}
</div>
);
}
// Usage
function App() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
);
}
https://www.builder.io/blog/nextjs-react-server-components
Builder.io
5 Misconceptions about React Server Components
Confused about React Server Components? You're not alone. Let's bust 5 myths and discover how to supercharge your Next.js apps with this new paradigm.
👍11👎2
Скрытые утечки памяти в React: как компилятор не спасет вас
Кевин Шинер недавно писал про useCallback и утечки памяти. Если кратко, то там было написано о том, что замыкания захватывают переменные из внешней области видимости, в React компонентах это означает захват пропсов, стейта или мемоизированных значений. Утечка памяти происходила, когда попеременно вызывался мемоизированный колбек, бесконечно создавая новую область видимости с ссылкой на старую.
Если запустить пример из предыдущей статьи после React Compiler, то утечки памяти не будет. Однако, React Compiler не спасет вас от утечки памяти. Автор изменил пример, чтобы добиться утечки памяти с использованием React Compiler:
Теперь при каждом изменении стейта будет создаваться новый объект bigData. Утечка памяти будет происходить все так же при попеременном вызове мемоизированных колбеков. Как скомпилируется новый пример:
Чтобы предотвратить утечку памяти можно отказаться от мемоизации колбеков. Однако с React Compiler это сделать не получится.
https://schiener.io/2024-07-07/react-closures-compiler
Кевин Шинер недавно писал про useCallback и утечки памяти. Если кратко, то там было написано о том, что замыкания захватывают переменные из внешней области видимости, в React компонентах это означает захват пропсов, стейта или мемоизированных значений. Утечка памяти происходила, когда попеременно вызывался мемоизированный колбек, бесконечно создавая новую область видимости с ссылкой на старую.
Если запустить пример из предыдущей статьи после React Compiler, то утечки памяти не будет. Однако, React Compiler не спасет вас от утечки памяти. Автор изменил пример, чтобы добиться утечки памяти с использованием React Compiler:
export const App = () => {
const [countA, setCountA] = useState(0);
const [countB, setCountB] = useState(0);
const bigData = new BigObject(`${countA}/${countB}`); // 10MB of data
// --snip--
};
Теперь при каждом изменении стейта будет создаваться новый объект bigData. Утечка памяти будет происходить все так же при попеременном вызове мемоизированных колбеков. Как скомпилируется новый пример:
const App = () => {
const $ = compilerRuntimeExports.c(24);
const [countA, setCountA] = reactExports.useState(0);
const [countB, setCountB] = reactExports.useState(0);
const t0 = `${countA}/${countB}`;
let t1;
if ($[0] !== t0) {
t1 = new BigObject(t0);
$[0] = t0;
$[1] = t1;
} else {
t1 = $[1];
}
const bigData = t1;
// ...
};
Чтобы предотвратить утечку памяти можно отказаться от мемоизации колбеков. Однако с React Compiler это сделать не получится.
https://schiener.io/2024-07-07/react-closures-compiler
Telegram
Заметки про React
useCallback и утечки памяти
В своем блоге Кевин Шинер поделился об опыте поиска утечек памяти при использовании useCallback. Рассмотрим пример:
import { useState, useCallback } from "react";
class BigObject {
public readonly data = new Uint8Array(1024…
В своем блоге Кевин Шинер поделился об опыте поиска утечек памяти при использовании useCallback. Рассмотрим пример:
import { useState, useCallback } from "react";
class BigObject {
public readonly data = new Uint8Array(1024…
👍17❤1
Изучите Suspense создавая хук useFetch
В своем блоге Слава Князев поэтапно создал хук useFetch для демонстрации того, как работает Suspense. Также автор показал, как сделать кэширование запросов, чтобы избежать частых запросов и рассинхрона данных.
В результате получился хук, который работает с Suspense и ErrorBoundary: если запрос находится в статусе pending, то покажется fallback Suspense, а если в запросе произойдет ошибка, то покажется fallback ErrorBoundary.
https://www.bbss.dev/posts/react-learn-suspense/
В своем блоге Слава Князев поэтапно создал хук useFetch для демонстрации того, как работает Suspense. Также автор показал, как сделать кэширование запросов, чтобы избежать частых запросов и рассинхрона данных.
В результате получился хук, который работает с Suspense и ErrorBoundary: если запрос находится в статусе pending, то покажется fallback Suspense, а если в запросе произойдет ошибка, то покажется fallback ErrorBoundary.
const useFetch = (url) => {
const { fetchUrl } = useContext(fetchCacheContext)
const promise = fetchUrl(url)
const state = readPromiseState(promise)
// Throw pending promise
const isPending = state.status === "pending"
if (isPending) throw promise
// Throw rejection reason
const error = state.reason
if (error) throw error
const data = state.value
// Allow refreshing data
const reload = () => fetchUrl(url, true)
// Only return data now
return [data, reload]
}
https://www.bbss.dev/posts/react-learn-suspense/
Building Better Software Slower
Learn Suspense by Building a Suspense-Enabled Library
Suspense has been a feature in React since v16.6.0. Despite this, I haven’t seen much of it in action beyond limited applications of “suspense-enabled libraries”. The React team seems to think Suspense is so incomplete that the entire API remains undocumented.…
👍18👎6
Отличия React и React Native
Хоть React и React Native имеют много общего, внутри они довольно разные. В блоге Expo вышла статья о различиях между React и React Native, которая поможет составить общую картину о разработке нативных приложений на React Native и Expo.
В нативных приложениях нельзя использовать HTML теги, вместо них используются UI примитивы:
В нативных приложениях нет глобальных стилей. Все стили инлайновые и передаются в элемент через тег style:
Также по умолчанию все элементы имеют стиль display: flex. Флексы работают немного иначе чем на вебе, например, flexDirection по умолчанию column (вместо row).
При начале работ на React Native новички допускают ошибки:
🔴 Использование
🔴 Используют элемент Button из React Native. Этот элемент плохо стилизуется и не практичен. Вместо него используйте TouchableOpacity.
🔴 Используют логические операторы AND (&&). Если случайно в них вернуть '', NaN или 0, то React Native не отрендерит их как текст. В этом случае произойдет крах приложения.
https://expo.dev/blog/from-web-to-native-with-react
Хоть React и React Native имеют много общего, внутри они довольно разные. В блоге Expo вышла статья о различиях между React и React Native, которая поможет составить общую картину о разработке нативных приложений на React Native и Expo.
В нативных приложениях нельзя использовать HTML теги, вместо них используются UI примитивы:
// Блок
<div></div>
<View></View>
// Изображение
<img src="https://domain.com/static/my-image.png" />
<Image source={{ uri: "https://domain.com/static/my-image.png" }} />
// Текст
<p>Hello</p>
<Text>Hello</Text>
// Список
array.map(item => <span>>{item.name}</span>)
<FlatList
data={posts}
renderItem={({ item }) => (
<Text>{item.name}</Text>
)}
/>
В нативных приложениях нет глобальных стилей. Все стили инлайновые и передаются в элемент через тег style:
export function MyComponent() {
return (
<View style={styles.container}>
<Text style={styles.greeting}>Set Reminder</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
padding: 24,
},
greeting: {
fontSize: 24
},
});
Также по умолчанию все элементы имеют стиль display: flex. Флексы работают немного иначе чем на вебе, например, flexDirection по умолчанию column (вместо row).
При начале работ на React Native новички допускают ошибки:
array.map. Если нужно отрендерить список элементов, то вместо array.map нужно использовать FlatList, который умеет оптимально рендерить большие списки и поддерживает виртуализацию.https://expo.dev/blog/from-web-to-native-with-react
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20
react-html
В React появился отдельный модуль react-html, из которого можно импортировать функцию рендера HTML renderToMarkup. Это преемник функции renderToStaticMarkup из react-dom.
Функцию renderToMarkup можно будет использовать как в React Native, так и с React Server Components. Эта функция рендерит HTML код, который не предназначен для гидратации. Функцию renderToMarkup можно использовать для генерации e-mail в Server Action:
💬 https://github.com/facebook/react/pull/30105
https://github.com/facebook/react/tree/main/packages/react-html
В React появился отдельный модуль react-html, из которого можно импортировать функцию рендера HTML renderToMarkup. Это преемник функции renderToStaticMarkup из react-dom.
Функцию renderToMarkup можно будет использовать как в React Native, так и с React Server Components. Эта функция рендерит HTML код, который не предназначен для гидратации. Функцию renderToMarkup можно использовать для генерации e-mail в Server Action:
import { renderToMarkup } from 'react-html';
import EmailTemplate from './my-email-template-component.js'
async function action(email, name) {
"use server";
// ... in your server, e.g. a Server Action...
const htmlString = await renderToMarkup(<EmailTemplate name={name} />);
// ... send e-mail using some e-mail provider
await sendEmail({ to: email, contentType: 'text/html', body: htmlString });
}
https://github.com/facebook/react/tree/main/packages/react-html
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
Получение данных с помощью Server Actions в Next.js
В своем блоге Робин Вирух показал опыт использования Server Actions для получения данных в Next.js. В документации Next.js рекомендуют использовать Route Handlers для создания API, например:
У этого подхода есть свои минусы: функцию fetchPosts необходимо отдельно типизировать.
В документации Next.js написано, что Server Actions предназначены только для записи (мутация), а не для чтения (запроса) данных. Однако, возможность использовать Server Actions для чтения данных есть, пример:
Плюсы такого подхода:
🔴 Есть возможность переиспользовать getPosts как в серверных компонентах, так и в клиентских компонентах.
🔴 Типизация запроса. Это похоже на типизированный удаленный вызов процедуры tRPC, аргументы функции и возвращаемый результат будут иметь тип.
https://www.robinwieruch.de/next-server-actions-fetch-data/
В своем блоге Робин Вирух показал опыт использования Server Actions для получения данных в Next.js. В документации Next.js рекомендуют использовать Route Handlers для создания API, например:
import { getPosts } from "@/data";
export async function GET() {
const posts = await getPosts();
return Response.json(posts);
}
"use client";
import { Posts } from "@/posts";
import { useQuery } from "@tanstack/react-query";
const fetchPosts = async () => {
const response = await fetch("/api/posts");
return await response.json();
};
const Page = () => {
const { data, isLoading } = useQuery({
queryKey: ["posts-route-handler"],
queryFn: fetchPosts,
});
/* --snip-- */
}
У этого подхода есть свои минусы: функцию fetchPosts необходимо отдельно типизировать.
В документации Next.js написано, что Server Actions предназначены только для записи (мутация), а не для чтения (запроса) данных. Однако, возможность использовать Server Actions для чтения данных есть, пример:
"use client";
import { getPosts } from "@/data";
import { Posts } from "@/posts";
import { useQuery } from "@tanstack/react-query";
const fetchPosts = async () => {
return await getPosts();
};
const Page = () => {
const { data, isLoading } = useQuery({
queryKey: ["posts-server-action"],
queryFn: fetchPosts,
});
/* --snip-- */
}
Плюсы такого подхода:
https://www.robinwieruch.de/next-server-actions-fetch-data/
Please open Telegram to view this post
VIEW IN TELEGRAM
www.robinwieruch.de
Data Fetching with Server Actions in Next.js
Can I fetch data with Server Actions in Next.js? There are different ways to fetch data. Normally Server Actions are used to mutate data, but ...
👍10👎3
DRY – источник плохих абстракций
В блоге Свижец Теллер вышла статья про принцип DRY. Самый худший и сложный для сопровождения код, который я видел или писал, был написан в погоне за DRY - Don't Repeat Yourself. Это один из первых принципов проектирования, который изучают разработчики и используют повсеместно.
⭐️ Бездумное использование принципа DRY приводит к созданию плохих абстракций в коде. Из-за чего код становится запутанным и сложно поддерживаемым.
Например, нужно показать навигационное меню:
Если пойти в сторону принципа DRY, то можно написать функцию-фабрику создания пункта меню и использовать массив:
Теперь добавление и удаление элементов меню стало проще, однако читать код стало тяжелее, т.к. приходится прыгать по строкам кода.
Представьте, что поступила задача – подсветить кнопку Buy красным цветом. Однако в текущем виде абстракция оптимизирована на отображение всех кнопок в одинаковом стиле. Можно попытаться добавить новые параметры в функцию-фабрику или попробовать переписать всю абстракцию. Но это усложнит код и будет проще вернуться к первому варианту кода.
https://swizec.com/blog/dry-the-common-source-of-bad-abstractions/
В блоге Свижец Теллер вышла статья про принцип DRY. Самый худший и сложный для сопровождения код, который я видел или писал, был написан в погоне за DRY - Don't Repeat Yourself. Это один из первых принципов проектирования, который изучают разработчики и используют повсеместно.
Например, нужно показать навигационное меню:
// 1
const NavigationMenu = () => {
return (
<ul>
<li>
<a href="/about">
<img src="question-icon.png" />
About
</a>
</li>
<li>
<a href="/contact">
<img src="person-icon.png" />
Contact
</a>
</li>
<li>
<a href="/buy">
<img src="cash-icon.png" />
Buy
</a>
</li>
// ...
</ul>
)
}
Если пойти в сторону принципа DRY, то можно написать функцию-фабрику создания пункта меню и использовать массив:
// 2
const NavigationMenu = () => {
const items = [
makeNavItem("/about", "question-icon.png", "About"),
makeNavItem("/contact", "person-icon.png", "Contact"),
makeNavItem("/buy", "cash-icon.png", "Buy"),
// ...
]
return (
<ul>
{items.map((item) => (
<li>
<a href={item.url}>
<img src={item.icon} />
{label}
</a>
</li>
))}
</ul>
)
}
Теперь добавление и удаление элементов меню стало проще, однако читать код стало тяжелее, т.к. приходится прыгать по строкам кода.
Представьте, что поступила задача – подсветить кнопку Buy красным цветом. Однако в текущем виде абстракция оптимизирована на отображение всех кнопок в одинаковом стиле. Можно попытаться добавить новые параметры в функцию-фабрику или попробовать переписать всю абстракцию. Но это усложнит код и будет проще вернуться к первому варианту кода.
https://swizec.com/blog/dry-the-common-source-of-bad-abstractions/
Please open Telegram to view this post
VIEW IN TELEGRAM
Swizec
DRY – the common source of bad abstractions | Swizec Teller
Swizec reveals the hidden pitfalls of overusing the DRY principle in coding, leading to bad abstractions. Discover how to write adaptable, efficient code that avoids these common traps.
👍30👎11❤1
Фича флаги в Next.js
Для Next.js появилась библиотека
Пример использования:
Библиотека не зависит от источника данных для фича-флагов, функция decide может быть асинхронной. Можно использовать как env переменные, так и делать запросы в провайдер данных для фича-флага:
https://vercel.com/blog/flags-as-code-in-next-js
Для Next.js появилась библиотека
@vercel/flags/next для работы с фича-флагами в коде. Пример объявления:
import { unstable_flag as flag } from "@vercel/flags/next";
export const showBanner = flag({
key: "banner",
decide: () => false,
});
Пример использования:
import { showBanner } from "../flags";
export default async function Page() {
const banner = await showBanner();
return (
<div>
{banner ? <Banner /> : null}
{/* other components */}
</div>
);
}
Библиотека не зависит от источника данных для фича-флагов, функция decide может быть асинхронной. Можно использовать как env переменные, так и делать запросы в провайдер данных для фича-флага:
export const showBanner = flag({
key: "banner",
async decide () {
await featureFlagClient.init();
return featureFlagClient.get(this.key);
}
});
https://vercel.com/blog/flags-as-code-in-next-js
Vercel
Flags as Code in Next.js - Vercel
The Flags SDK is a free open-source library that gives developers the tools they need to use feature flags in Next.js and SvelteKit applications.
👍13🔥8❤1👎1
Гибкая предварительная загрузка данных в SPA
При создании большого SPA одним из важных пунктов оптимизации становится предварительная загрузка данных. Например, предварительная загрузка пользовательских данных, конфига и т.д. поможет быстрее отобразить страницу.
В SPA приложениях React чаще всего загрузка данных происходит после монтирования приложения. Этот подход может стать неэффективным по мере масштабирования приложения.
В своей статье автор поделился скриптом для предварительной загрузки данных. Этот скрипт вставляется в тег <head> и исполняется одним из первых. Базовая реализация:
В своем блоге автор поделился более низкоуровневой реализацией скрипта.
https://mmazzarolo.com/blog/2024-07-29-data-preloading-script/
При создании большого SPA одним из важных пунктов оптимизации становится предварительная загрузка данных. Например, предварительная загрузка пользовательских данных, конфига и т.д. поможет быстрее отобразить страницу.
В SPA приложениях React чаще всего загрузка данных происходит после монтирования приложения. Этот подход может стать неэффективным по мере масштабирования приложения.
В своей статье автор поделился скриптом для предварительной загрузки данных. Этот скрипт вставляется в тег <head> и исполняется одним из первых. Базовая реализация:
<head>
<script>
// Упрощенная версия, чтобы показать как высокоуровнево работает загрузка
window.__userDataPromise = (async function () {
const user = await (await fetch("/api/user")).json();
const userPreferences = await (await fetch(`/api/user-preferences/${user.id}`)).json();
return { user, userPreferences };
})();
</script>
</head>
function MyApp() {
const [userData, setUserData] = useState();
async function loadUserData() {
setUserData(await window.__userDataPromise);
}
useEffect(() => {
loadUserData();
}, []);
}
В своем блоге автор поделился более низкоуровневой реализацией скрипта.
https://mmazzarolo.com/blog/2024-07-29-data-preloading-script/
Mmazzarolo
Flexible network data preloading in large SPAs
Sharing my experience with a pattern I’ve used multiple times to implement flexible network data preloading in large client-side-rendered SPAs.
👍19
Рефакторинг запутанного компонента
В своем блоге Алекс Кондов рассказал, как бы он проводил рефакторинг запутанного компонента формы. Кратко, о чем он пишет:
- Написать тесты. Если еще тестов нет, то перед рефакторингом надо написать тесты для компонента. В тестах должна проверяться бизнес логика. Делается это для того, чтобы знать, что в процессе рефакторинга ничего не сломается.
- Использовать линтер. Нужно добавить автоматические проверки, которые подсветят проблемный код. Например, неиспользуемые импорты, значения стейта и т.д.
- Удалить «мертвый» код. Нужно удалить лишние комментарии, неиспользуемые переменные. В этом как раз поможет линтер.
- Сократить количество стейтов. Если в компоненте много useState, то скорее всего он делает слишком много разных вещей. В этом случае его можно разделить на подкомпоненты и связать через пропсы.
- Убрать большие условные рендеры. Если блоки похожи, то их можно объединить в один компонент. Если блоки различны, то лучше разделить их на два компонента и разделить логику между ними.
- Следить за ответственностью компонента, чтобы внутри него было как можно меньше действий. В дальнейшем это упростит понимание кода.
- Вынести утилитарные функции за пределы компонент. Функция может находиться в теле компонента, только если ей требуется доступ к стейту или другому значению внутри компонента.
- Используйте для валидации форм декларативные схемы, например Zod, Yup.
- Не используйте inline стили.
https://alexkondov.com/refactoring-a-messy-react-component/
В своем блоге Алекс Кондов рассказал, как бы он проводил рефакторинг запутанного компонента формы. Кратко, о чем он пишет:
- Написать тесты. Если еще тестов нет, то перед рефакторингом надо написать тесты для компонента. В тестах должна проверяться бизнес логика. Делается это для того, чтобы знать, что в процессе рефакторинга ничего не сломается.
- Использовать линтер. Нужно добавить автоматические проверки, которые подсветят проблемный код. Например, неиспользуемые импорты, значения стейта и т.д.
- Удалить «мертвый» код. Нужно удалить лишние комментарии, неиспользуемые переменные. В этом как раз поможет линтер.
- Сократить количество стейтов. Если в компоненте много useState, то скорее всего он делает слишком много разных вещей. В этом случае его можно разделить на подкомпоненты и связать через пропсы.
- Убрать большие условные рендеры. Если блоки похожи, то их можно объединить в один компонент. Если блоки различны, то лучше разделить их на два компонента и разделить логику между ними.
return !numberIncorrect ? (
// A lot of JSX...
) : (
// Even more JSX...
)
- Следить за ответственностью компонента, чтобы внутри него было как можно меньше действий. В дальнейшем это упростит понимание кода.
- Вынести утилитарные функции за пределы компонент. Функция может находиться в теле компонента, только если ей требуется доступ к стейту или другому значению внутри компонента.
- Используйте для валидации форм декларативные схемы, например Zod, Yup.
- Не используйте inline стили.
https://alexkondov.com/refactoring-a-messy-react-component/
Alexkondov
Common Sense Refactoring of a Messy React Component
One thing I’ve learned from all the consulting I’ve done is that rewrites rarely lead to anything good. Almost in all cases, when you have an application…
👍16🔥3❤1👎1
Чеклист безопасности React приложения
Обзор популярных уязвимостей фронтенд приложений в контексте React:
🔴 Cross-Site Scripting (XSS). Атака заключающаяся в внедрении вредоносного кода, который вызывается на странице у пользователя. Связано это с тем, что выводимые данные не обрабатывается должным образом. React хорошо защищен от XSS атак, однако все равно уязвим из-за неправильного использования функций, сторонних библиотек или нестандартных реализаций. При использовании dangerouslySetInnerHTML санитизируйте передаваемые данные, например, через библиотеку dompurify:
🔴 Content Security Policy (CSP) заголовок. Использование CSP заголовка защитит от большинства веб-атак, включая XSS. CSP заголовок позволяет указать какие ресурсы могут загружаться и исполняться браузером. Позволяет работать в Report-Only режиме, позволяя только собирать обнаруженные проблемы, не применяя правила. Пример:
🔴 Cross-Site Request Forgery (CSRF). При CSRF атаке злоумышленник обманом заставляет пользователя отправить запрос, который он не собирался делать. Обычно это делается путем внедрения вредоносных запросов на сайт или e-mail, с которым взаимодействует пользователь. Для противодействия CSRF атаке используются токены, которые генерируются на странице и передаются вместе с запросом. Пример:
🔴 Переменные окружения. Используйте env переменные и не храните их значения в коде в репозитории.
https://www.trevorlasn.com/blog/frontend-security-checklist
Обзор популярных уязвимостей фронтенд приложений в контексте React:
import DOMPurify from 'dompurify';
const comment = '<script>alert("XSS Attack!")</script>Great article!';
const UserComments = () => {
return (
<div>
<li dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(comment) }} />
</div>
);
};
header Content-Security-Policy "script-src 'self' 'unsafe-inline' https://maps.googleapis.com https://www.googletagmanager.com;"
function CSRFProtectedForm() {
const [csrfToken, setCsrfToken] = useState('');
{/* Запрос CSRF токена */}
return (
<form onSubmit={submitForm}>
<input type="hidden" name="_csrf" value={csrfToken} />
{/* --snip-- */}
<button type="submit">Submit</button>
</form>
);
}
https://www.trevorlasn.com/blog/frontend-security-checklist
Please open Telegram to view this post
VIEW IN TELEGRAM
Google for Developers
Google Maps Platform | Google for Developers
Millions of websites and apps use Google Maps Platform to power location experiences for their users.
👍23❤2👎2
Глубокое погружение в формы в современном React
В своем блоге Кент С. Доддс рассказал, как использовать новые хуки useFormStatus, useActionState и useOptimistic:
🌟 useFormStatus. Если вы знакомы с контекстом в React, то представьте, что тег form - это провайдер данных, а useFormStatus - это хук, который имеет доступ к данным провайдера. С помощью хука можно получить информацию о состоянии формы и переданных данных. Минус хука в том, что, чтобы его использовать, нужно создавать отдельный компонент, который должен быть вложен внутри form.
🌟 useActionState. Этот хук позволяет обновить стейт по данным из формы. Разберем пример:
Хук принимает функцию joinEvent, начальный стейт
https://www.epicreact.dev/react-forms
В своем блоге Кент С. Доддс рассказал, как использовать новые хуки useFormStatus, useActionState и useOptimistic:
function JoinButton({ children }: { children: React.ReactNode }) {
const { pending, data } = useFormStatus()
return (
<button type="submit">
{pending ? `Joining as ${data.get('username')}...` : children}
</button>
)
}
function JoinEventForm() {
/** --snip */
return (
<form action={joinEvent}>
<JoinButton>Join</JoinButton>
</form>
)
}
async function joinEvent(
previousState: { joined: boolean },
formData: FormData,
) {
/** --snip */
return { joined: true }
}
function JoinEventForm() {
const [state, formAction, isPending] = useActionState(
joinEvent,
{ joined: false },
JOIN_URL,
)
return (
<div>
{state.joined ? (
<p>See you there!</p>
) : (
<form action={formAction}>
<button type="submit">
{isPending ? 'Joining...' : 'Join'}
</button>
</form>
)}
</div>
)
}
Хук принимает функцию joinEvent, начальный стейт
{ joined: false } и опциональный параметр пермалинк JOIN_URL. Он возвращает массив с текущим стейтом, функцию триггера действия и флаг – находится ли форма в состоянии отправки. Обратите внимание, что функция joinEvent принимает первым аргументом предыдущее состояние. В каком-то смысле можно представить, что это редьюсер для useReducer. https://www.epicreact.dev/react-forms
Please open Telegram to view this post
VIEW IN TELEGRAM
Epic React
A deep dive on forms with modern React
React 19 introduces terrific primitives for building great forms. Let's dive deep on forms for the web with React.
👍14
Создание стейт менеджера за 40 строк
Пример простого стейт менеджера для React, работающего через хук useSyncExternalStore.
Хук useSyncExternalStore появился в React 18 и позволяет подписаться на изменение внешнего хранилища. Внешним хранилищем может быть что угодно, например, браузер.
Значения в внешнем хранилище могут меняться, а с помощью хука эти изменения можно отследить. Пример стейт менеджера:
Если хук useCountStore будет использоваться в нескольких компонентах, то его значение будет общим между всеми компонентами.
https://paripsky.github.io/blog/build-your-own-react-state-management/
Пример простого стейт менеджера для React, работающего через хук useSyncExternalStore.
Хук useSyncExternalStore появился в React 18 и позволяет подписаться на изменение внешнего хранилища. Внешним хранилищем может быть что угодно, например, браузер.
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
Значения в внешнем хранилище могут меняться, а с помощью хука эти изменения можно отследить. Пример стейт менеджера:
import { useSyncExternalStore } from 'react';
export type Listener = () => void;
function createStore<T>({ initialState }: { initialState: T }) {
let subscribers: Listener[] = [];
let state = initialState;
const notifyStateChanged = () => {
subscribers.forEach((fn) => fn());
};
return {
subscribe(fn: Listener) {
subscribers.push(fn);
return () => {
subscribers = subscribers.filter((listener) => listener !== fn);
};
},
getSnapshot() {
return state;
},
setState(newState: T) {
state = newState;
notifyStateChanged();
},
};
}
export function createUseStore<T>(initialState: T) {
const store = createStore({ initialState });
return () => [useSyncExternalStore(store.subscribe, store.getSnapshot), store.setState] as const;
}
export const useCountStore = createUseStore(0);
function Counter() {
const [count, setCount] = useCountStore();
const increment = () => {
setCount(count + 1);
};
/* --snip-- */
}
Если хук useCountStore будет использоваться в нескольких компонентах, то его значение будет общим между всеми компонентами.
https://paripsky.github.io/blog/build-your-own-react-state-management/
paripsky.github.io
Build your own React state management library in under 40 lines of code
A guide on how to built a custom state management solution for react in under 40 lines of code with typescript support
👍17❤2
Google Translate может сломать React приложение
Браузерное расширение Google Translate может сломать React приложение. Это связано с тем, что, когда расширение активируется, он ищет узлы TextNode для перевода. Эти узлы заменяются на FontElement с переведенными строками внутри. Например:
Как видно из примера, меняется DOM структура элемента. Исходный TextNode размонтируется и заменяется новым FontElement с переведенным текстом внутри.
Какие могут возникнуть проблемы при активированном Google Translate:
🔴 Значения стейта useState на странице может не обновляться. Если расширение активировано, то оно размонтирует узел и заменяет DOM элемент. Однако размонтированный DOM узел остается в памяти приложения и изменения стейта происходят в нем.
🔴 Краш приложения:
NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
Это может произойти из-за условного рендера строки, например:
🔴 Неправильное значение в
Кроме Google Translate приложение могут сломать и другие расширения, которые манипулируют DOM. На данный момент нет реального решения этой проблемы. Есть лишь некоторые способы обхода, но они создают дополнительные проблемы. Например, один из более-менее простых способов обхода проблемы заключается в оборачивании условного рендера строки в span.
https://martijnhols.nl/gists/everything-about-google-translate-crashing-react
Браузерное расширение Google Translate может сломать React приложение. Это связано с тем, что, когда расширение активируется, он ищет узлы TextNode для перевода. Эти узлы заменяются на FontElement с переведенными строками внутри. Например:
<p>There are 4 lights!</p>
// После перевода:
<p><font>Er zijn 4 lampen!</font></p>
Как видно из примера, меняется DOM структура элемента. Исходный TextNode размонтируется и заменяется новым FontElement с переведенным текстом внутри.
Какие могут возникнуть проблемы при активированном Google Translate:
NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
Это может произойти из-за условного рендера строки, например:
{lightsOn && 'There are 4 lights!’}event.target. Когда Google Translate активируется, то event.target у текста становится элемент font, а не ожидаемый элемент, в котором был изначальный текст. Кроме Google Translate приложение могут сломать и другие расширения, которые манипулируют DOM. На данный момент нет реального решения этой проблемы. Есть лишь некоторые способы обхода, но они создают дополнительные проблемы. Например, один из более-менее простых способов обхода проблемы заключается в оборачивании условного рендера строки в span.
https://martijnhols.nl/gists/everything-about-google-translate-crashing-react
Please open Telegram to view this post
VIEW IN TELEGRAM
Martijn Hols
Everything about Google Translate crashing React (and other web apps) by Martijn Hols
A deep dive into Google Translate (and other browser extensions) interference breaking React and other web apps.
👍28❤1