diff --git a/src/content/reference/react/useTransition.md b/src/content/reference/react/useTransition.md index 6b7c511e7..ceb7849ce 100644 --- a/src/content/reference/react/useTransition.md +++ b/src/content/reference/react/useTransition.md @@ -3,8 +3,7 @@ title: useTransition --- - -`useTransition` is a React Hook that lets you render a part of the UI in the background. +`useTransition` — это React-хук, который позволяет выполнять рендеринг части пользовательского интерфейса в фоновом режиме. ```js const [isPending, startTransition] = useTransition() @@ -16,11 +15,11 @@ const [isPending, startTransition] = useTransition() --- -## Reference {/*reference*/} +## Справочник {/*reference*/} ### `useTransition()` {/*usetransition*/} -Call `useTransition` at the top level of your component to mark some state updates as Transitions. +Вызовите `useTransition` на верхнем уровне вашего компонента, чтобы пометить некоторые обновления состояния как Переходы. ```js import { useTransition } from 'react'; @@ -31,24 +30,24 @@ function TabContainer() { } ``` -[See more examples below.](#usage) +[Больше примеров ниже.](#usage) -#### Parameters {/*parameters*/} +#### Параметры {/*parameters*/} -`useTransition` does not take any parameters. +`useTransition` не принимает никаких параметров. -#### Returns {/*returns*/} +#### Возвращаемое значение {/*returns*/} -`useTransition` returns an array with exactly two items: +`useTransition` возвращает массив ровно из двух элементов: -1. The `isPending` flag that tells you whether there is a pending Transition. -2. The [`startTransition` function](#starttransition) that lets you mark updates as a Transition. +1. Флаг `isPending`, который указывает, есть ли ожидающий Переход. +2. [Функция `startTransition`](#starttransition), которая позволяет помечать изменения как Переход. --- ### `startTransition(action)` {/*starttransition*/} -The `startTransition` function returned by `useTransition` lets you mark an update as a Transition. +Функция `startTransition`, которую возвращает `useTransition`, позволяет помечать изменения как Переход. ```js {6,8} function TabContainer() { @@ -65,9 +64,9 @@ function TabContainer() { ``` -#### Functions called in `startTransition` are called "Actions". {/*functions-called-in-starttransition-are-called-actions*/} +#### Функции, вызываемые в `startTransition` называются «Действия»("Actions"). {/*functions-called-in-starttransition-are-called-actions*/} -The function passed to `startTransition` is called an "Action". By convention, any callback called inside `startTransition` (such as a callback prop) should be named `action` or include the "Action" suffix: +Функция, переданная `startTransition`называется «Действие» ("Action"). По соглашению, любой колбэк, вызываемый внутри startTransition (например, колбэк-проп), должен называться `action` или иметь суффикс "Action" в имени: ```js {1,9} function SubmitButton({ submitAction }) { @@ -82,7 +81,7 @@ function SubmitButton({ submitAction }) { }); }} > - Submit + Отправить ); } @@ -93,37 +92,37 @@ function SubmitButton({ submitAction }) { -#### Parameters {/*starttransition-parameters*/} +#### Параметры {/*starttransition-parameters*/} -* `action`: A function that updates some state by calling one or more [`set` functions](/reference/react/useState#setstate). React calls `action` immediately with no parameters and marks all state updates scheduled synchronously during the `action` function call as Transitions. Any async calls that are awaited in the `action` will be included in the Transition, but currently require wrapping any `set` functions after the `await` in an additional `startTransition` (see [Troubleshooting](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). State updates marked as Transitions will be [non-blocking](#marking-a-state-update-as-a-non-blocking-transition) and [will not display unwanted loading indicators](#preventing-unwanted-loading-indicators). +* `action`: Функция, которая обновляет некоторое состояние, вызывая одну или несколько [функций `set`.](/reference/react/useState#setstate) React немедленно вызывает `action` без параметров и помечает все обновления состояния, которые были синхронно запланированы во время вызова функции `action`, как Переходы. Любые асинхронные вызовы, которые ожидаются в `action`, будут включены в Переход, но в настоящее время требуют обёртывания любых функций `set` после `await` в дополнительный startTransition (см. [Устранение неполадок](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). Обновления состояния, помеченные как Переходы, будут [неблокирующими](#marking-a-state-update-as-a-non-blocking-transition) и [не будут отображать нежелательные индикаторы загрузки](#preventing-unwanted-loading-indicators). -#### Returns {/*starttransition-returns*/} +#### Возвращаемое значение {/*starttransition-returns*/} -`startTransition` does not return anything. +`startTransition` ничего не возвращает. -#### Caveats {/*starttransition-caveats*/} +#### Подводные камни {/*starttransition-caveats*/} -* `useTransition` is a Hook, so it can only be called inside components or custom Hooks. If you need to start a Transition somewhere else (for example, from a data library), call the standalone [`startTransition`](/reference/react/startTransition) instead. +* `useTransition` — это хук, поэтому его можно вызывать только внутри компонентов или пользовательских хуков. Если вам нужно запустить Переход где-то ещё (например, из библиотеки данных), вместо этого вызовите автономный [`startTransition`](/reference/react/startTransition). -* You can wrap an update into a Transition only if you have access to the `set` function of that state. If you want to start a Transition in response to some prop or a custom Hook value, try [`useDeferredValue`](/reference/react/useDeferredValue) instead. +* Вы можете обернуть обновление в Переход, только если у вас есть доступ к функции `set` этого состояния. Если вы хотите запустить Переход в ответ на какой-либо проп или значение пользовательского хука, попробуйте вместо этого [`useDeferredValue`](/reference/react/useDeferredValue). -* The function you pass to `startTransition` is called immediately, marking all state updates that happen while it executes as Transitions. If you try to perform state updates in a `setTimeout`, for example, they won't be marked as Transitions. +* Функция, которую вы передаёте в `startTransition`, вызывается немедленно, помечая все обновления состояния, которые происходят во время его выполнения, как Переходы. Если вы попытаетесь обновить состояния в, например, `setTimeout`, то они не будут помечены как Переходы. -* You must wrap any state updates after any async requests in another `startTransition` to mark them as Transitions. This is a known limitation that we will fix in the future (see [Troubleshooting](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). +* Вы должны обернуть любые обновления состояния после асинхронных запросов в дополнительный `startTransition`, чтобы пометить их как Переходы. Это известное ограничение, которое мы планируем исправить в будущем (см. [Устранение неполадок](#react-doesnt-treat-my-state-update-after-await-as-a-transition)). -* The `startTransition` function has a stable identity, so you will often see it omitted from Effect dependencies, but including it will not cause the Effect to fire. If the linter lets you omit a dependency without errors, it is safe to do. [Learn more about removing Effect dependencies.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) +* Функция `startTransition` имеет стабильную идентичность, поэтому вы часто увидите, что её опускают в зависимостях Эффектов, но добавление её в список зависимостей не вызовет повторного срабатывания Эффекта. Если линтер позволяет опустить зависимость без ошибок, то это можно смело делать. [Узнайте больше о том, как удалять зависимости Эффекта.](/learn/removing-effect-dependencies#move-dynamic-objects-and-functions-inside-your-effect) -* A state update marked as a Transition will be interrupted by other state updates. For example, if you update a chart component inside a Transition, but then start typing into an input while the chart is in the middle of a re-render, React will restart the rendering work on the chart component after handling the input update. +* Обновление состояния, помеченное как Переход, будет прервано другими обновлениями состояния. Например, если вы обновляете компонент диаграммы внутри Перехода, а затем начинаете вводить текст в поле ввода, когда диаграмма находится в середине повторного рендера, React перезапустит работу по рендерингу компонента диаграммы после обработки обновления поля ввода. -* Transition updates can't be used to control text inputs. +* Обновления Перехода не могут быть использованы для управления текстовыми полями ввода. -* If there are multiple ongoing Transitions, React currently batches them together. This is a limitation that may be removed in a future release. +* Если существует несколько активных Переходов, React, в настоящее время, группирует их вместе. Это ограничение, вероятно, будет убрано в будущих версиях. -## Usage {/*usage*/} +## Использование {/*usage*/} -### Perform non-blocking updates with Actions {/*perform-non-blocking-updates-with-actions*/} +### Выполняйте неблокирующие изменения с помощью Действий. {/*marking-a-state-update-as-a-non-blocking-transition*/} -Call `useTransition` at the top of your component to create Actions, and access the pending state: +Вызовите `useTransition` на верхнем уровне вашего компонента, чтобы создать Действие и получить доступ к состоянию ожидания. ```js [[1, 4, "isPending"], [2, 4, "startTransition"]] import {useState, useTransition} from 'react'; @@ -134,12 +133,12 @@ function CheckoutForm() { } ``` -`useTransition` returns an array with exactly two items: +`useTransition` возвращает массив ровно из двух элементов: -1. The `isPending` flag that tells you whether there is a pending Transition. -2. The `startTransition` function that lets you create an Action. +1. Флаг `isPending` указывает, есть ли ожидающий Переход. +2. Функция `startTransition` позволяет создать Действие. -To start a Transition, pass a function to `startTransition` like this: +Чтобы начать Переход, передайте функцию в `startTransition` следующим образом: ```js import {useState, useTransition} from 'react'; @@ -161,17 +160,17 @@ function CheckoutForm() { } ``` -The function passed to `startTransition` is called the "Action". You can update state and (optionally) perform side effects within an Action, and the work will be done in the background without blocking user interactions on the page. A Transition can include multiple Actions, and while a Transition is in progress, your UI stays responsive. For example, if the user clicks a tab but then changes their mind and clicks another tab, the second click will be immediately handled without waiting for the first update to finish. +Функция, передаваемая в `startTransition`, называется «Действие». Внутри Действия можно обновлять состояние и (опционально) выполнять побочные эффекты. Эта работа будет выполнена в фоновом режиме, не блокируя взаимодействие пользователя со страницей. Переход может включать несколько Действий, и пока Переход выполняется, ваш интерфейс остаётся отзывчивым. Например, если пользователь нажмёт на вкладку, а затем передумает и кликнет на другую — второй клик будет обработан немедленно, не дожидаясь завершения первого обновления. -To give the user feedback about in-progress Transitions, to `isPending` state switches to `true` at the first call to `startTransition`, and stays `true` until all Actions complete and the final state is shown to the user. Transitions ensure side effects in Actions to complete in order to [prevent unwanted loading indicators](#preventing-unwanted-loading-indicators), and you can provide immediate feedback while the Transition is in progress with `useOptimistic`. +Чтобы дать пользователю обратную связь о выполняющихся Переходах, состояние `isPending` переключается в `true` при первом вызове `startTransition` и остаётся `true`, пока все Действия не завершатся и финальное состояние не будет отображено пользователю. Переходы гарантируют, что побочные эффекты в Действиях выполняются последовательно, чтобы [избежать нежелательных индикаторов загрузки](#preventing-unwanted-loading-indicators). Также, во время выполнения Перехода можно обеспечить немедленную обратную связь с помощью `useOptimistic`. - + -#### Updating the quantity in an Action {/*updating-the-quantity-in-an-action*/} +#### Обновление количества в Действии {/*updating-the-quantity-in-an-action*/} -In this example, the `updateQuantity` function simulates a request to the server to update the item's quantity in the cart. This function is *artificially slowed down* so that it takes at least a second to complete the request. +В этом примере функция `updateQuantity` имитирует запрос к серверу для обновления количества товара в корзине. Эта функция *искусственно замедлена*, чтобы выполнение запроса занимало как минимум одну секунду. -Update the quantity multiple times quickly. Notice that the pending "Total" state is shown while any requests are in progress, and the "Total" updates only after the final request is complete. Because the update is in an Action, the "quantity" can continue to be updated while the request is in progress. +Попробуйте быстро несколько раз обновить количество. Обратите внимание, что состояние "Total" остаётся в ожидании, пока выполняются запросы, и обновляется только после завершения последнего запроса. Поскольку обновление выполняется внутри Действия, значение "quantity" можно продолжать изменять, даже пока запрос ещё выполняется. @@ -201,8 +200,8 @@ export default function App({}) { const [isPending, startTransition] = useTransition(); const updateQuantityAction = async newQuantity => { - // To access the pending state of a transition, - // call startTransition again. + // Чтобы получить доступ к состоянию ожидания перехода, + // вызовите startTransition ещё раз. startTransition(async () => { const savedQuantity = await updateQuantity(newQuantity); startTransition(() => { @@ -213,7 +212,7 @@ export default function App({}) { return (
-

Checkout

+

Оформление заказа


@@ -227,15 +226,15 @@ import { startTransition } from "react"; export default function Item({action}) { function handleChange(event) { - // To expose an action prop, call the callback in startTransition. + // Чтобы передать проп для действия, вызовите колбэк внутри startTransition. startTransition(async () => { action(event.target.value); }) } return (
- Eras Tour Tickets - + Билеты на концерты группы Era + - Total: + Итого: - {isPending ? "🌀 Updating..." : `${intl.format(quantity * 9999)}`} + {isPending ? "🌀 Обновляется..." : `${intl.format(quantity * 9999)}`}
) @@ -268,7 +267,7 @@ export default function Total({quantity, isPending}) { ```js src/api.js export async function updateQuantity(newQuantity) { return new Promise((resolve, reject) => { - // Simulate a slow network request. + // Имитируем медленный сетевой запрос. setTimeout(() => { resolve(newQuantity); }, 2000); @@ -305,22 +304,22 @@ export async function updateQuantity(newQuantity) { -This is a basic example to demonstrate how Actions work, but this example does not handle requests completing out of order. When updating the quantity multiple times, it's possible for the previous requests to finish after later requests causing the quantity to update out of order. This is a known limitation that we will fix in the future (see [Troubleshooting](#my-state-updates-in-transitions-are-out-of-order) below). +Это базовый пример, демонстрирующий, как работают Действия, но этот пример не обрабатывает завершение запросов в неправильном порядке. При обновлении количества несколько раз возможно, что предыдущие запросы завершатся после более поздних, что приведет к обновлению количества не в том порядке. Это известное ограничение, которое мы исправим в будущем (см. [Устранение неполадок](#my-state-updates-in-transitions-are-out-of-order) ниже). -For common use cases, React provides built-in abstractions such as: +Для распространённых случаев использования React предоставляет встроенные абстракции, такие как: - [`useActionState`](/reference/react/useActionState) - [`
` actions](/reference/react-dom/components/form) -- [Server Functions](/reference/rsc/server-functions) +- [Серверные функции](/reference/rsc/server-functions) -These solutions handle request ordering for you. When using Transitions to build your own custom hooks or libraries that manage async state transitions, you have greater control over the request ordering, but you must handle it yourself. +Эти решения сами обрабатывают порядок запросов. При использовании Переходов для создания собственных хуков или библиотек, управляющих асинхронными состояниями, у вас есть больший контроль над порядком запросов, но вы должны обрабатывать это самостоятельно. -#### Updating the quantity without an Action {/*updating-the-users-name-without-an-action*/} +#### Обновление количества без Действия {/*updating-the-users-name-without-an-action*/} -In this example, the `updateQuantity` function also simulates a request to the server to update the item's quantity in the cart. This function is *artificially slowed down* so that it takes at least a second to complete the request. +В этом примере функция `updateQuantity` также имитирует запрос к серверу для обновления количества товара в корзине. Эта функция *искусственно замедлена*, чтобы выполнение запроса занимало как минимум одну секунду. -Update the quantity multiple times quickly. Notice that the pending "Total" state is shown while any requests is in progress, but the "Total" updates multiple times for each time the "quantity" was clicked: +Попробуйте быстро несколько раз обновить количество. Обратите внимание, что состояние "Total" отображается в ожидании, пока выполняется любой запрос, но при этом "Total" обновляется несколько раз — каждый раз, когда нажимается кнопка изменения "quantity": @@ -350,7 +349,7 @@ export default function App({}) { const [isPending, setIsPending] = useState(false); const onUpdateQuantity = async newQuantity => { - // Manually set the isPending State. + // Ручное управление состоянием isPending. setIsPending(true); const savedQuantity = await updateQuantity(newQuantity); setIsPending(false); @@ -359,7 +358,7 @@ export default function App({}) { return (
-

Checkout

+

Оформление заказа


@@ -376,8 +375,8 @@ export default function Item({onUpdateQuantity}) { } return (
- Eras Tour Tickets - + Билеты на концерты группы Era + - Total: + Итого: - {isPending ? "🌀 Updating..." : `${intl.format(quantity * 9999)}`} + {isPending ? "🌀 Обновляется..." : `${intl.format(quantity * 9999)}`}
) @@ -410,7 +409,7 @@ export default function Total({quantity, isPending}) { ```js src/api.js export async function updateQuantity(newQuantity) { return new Promise((resolve, reject) => { - // Simulate a slow network request. + // Имитируем медленный сетевой запрос. setTimeout(() => { resolve(newQuantity); }, 2000); @@ -447,7 +446,7 @@ export async function updateQuantity(newQuantity) { -A common solution to this problem is to prevent the user from making changes while the quantity is updating: +Распространённое решение этой проблемы — запретить пользователю вносить изменения, пока количество обновляется: @@ -478,7 +477,7 @@ export default function App({}) { const onUpdateQuantity = async event => { const newQuantity = event.target.value; - // Manually set the isPending state. + // Ручное управление состоянием isPending. setIsPending(true); const savedQuantity = await updateQuantity(newQuantity); setIsPending(false); @@ -487,7 +486,7 @@ export default function App({}) { return (
-

Checkout

+

Оформление заказа


@@ -501,8 +500,8 @@ export default function App({}) { export default function Item({isPending, onUpdateQuantity}) { return (
- Eras Tour Tickets - + Билеты на концерты группы Era + - Total: + Итого: - {isPending ? "🌀 Updating..." : `${intl.format(quantity * 9999)}`} + {isPending ? "🌀 Обновляется..." : `${intl.format(quantity * 9999)}`}
) @@ -536,7 +535,7 @@ export default function Total({quantity, isPending}) { ```js src/api.js export async function updateQuantity(newQuantity) { return new Promise((resolve, reject) => { - // Simulate a slow network request. + // Имитируем медленный сетевой запрос. setTimeout(() => { resolve(newQuantity); }, 2000); @@ -573,7 +572,7 @@ export async function updateQuantity(newQuantity) { -This solution makes the app feel slow, because the user must wait each time they update the quantity. It's possible to add more complex handling manually to allow the user to interact with the UI while the quantity is updating, but Actions handle this case with a straight-forward built-in API. +Этот подход делает приложение медленным на ощупь, так как пользователю приходится ждать каждый раз, когда он изменяет количество. Можно вручную добавить более сложную логику, чтобы позволить пользователю взаимодействовать с интерфейсом, пока количество обновляется, но Действия решают эту задачу с помощью простого и встроенного API. @@ -581,12 +580,12 @@ This solution makes the app feel slow, because the user must wait each time they --- -### Exposing `action` prop from components {/*exposing-action-props-from-components*/} +### Передача пропа `action` из компонентов {/*exposing-action-props-from-components*/} -You can expose an `action` prop from a component to allow a parent to call an Action. +Вы можете передать проп `action` из компонента, чтобы родительский компонент мог вызывать Действие. +Например, компонент `TabButton` оборачивает свою логику `onClick` в проп `action`: -For example, this `TabButton` component wraps its `onClick` logic in an `action` prop: ```js {8-10} export default function TabButton({ action, children, isActive }) { @@ -606,7 +605,7 @@ export default function TabButton({ action, children, isActive }) { } ``` -Because the parent component updates its state inside the `action`, that state update gets marked as a Transition. This means you can click on "Posts" and then immediately click "Contact" and it does not block user interactions: +Поскольку родительский компонент обновляет своё состояние внутри `action`, это обновление состояния помечается как Переход. Это значит что, вы можете нажать на «Публикации», а затем сразу же нажать на «Контакты» — и это не блокирует взаимодействие с пользователем: @@ -625,19 +624,19 @@ export default function TabContainer() { isActive={tab === 'about'} action={() => setTab('about')} > - About + Обо мне setTab('posts')} > - Posts (slow) + Публикации (замедлена) setTab('contact')} > - Contact + Контакты
{tab === 'about' && } @@ -671,7 +670,7 @@ export default function TabButton({ action, children, isActive }) { ```js src/AboutTab.js export default function AboutTab() { return ( -

Welcome to my profile!

+

Добро пожаловать в мой профиль!

); } ``` @@ -680,8 +679,8 @@ export default function AboutTab() { import { memo } from 'react'; const PostsTab = memo(function PostsTab() { - // Log once. The actual slowdown is inside SlowPost. - console.log('[ARTIFICIALLY SLOW] Rendering 500 '); + // Логировать один раз. Фактическое замедление происходит внутри SlowPost. + console.log('[ИСКУССТВЕННО ЗАМЕДЛЕННО] Рендеринг 500 '); let items = []; for (let i = 0; i < 500; i++) { @@ -697,12 +696,12 @@ const PostsTab = memo(function PostsTab() { function SlowPost({ index }) { let startTime = performance.now(); while (performance.now() - startTime < 1) { - // Do nothing for 1 ms per item to emulate extremely slow code + // Ничего не делать в течение 1 мс за элемент, чтобы эмулировать чрезвычайно медленный код } return (
  • - Post #{index + 1} + Публикация №{index + 1}
  • ); } @@ -715,7 +714,7 @@ export default function ContactTab() { return ( <>

    - You can find me online here: + Найти меня в Интернете можно здесь:

    • admin@mysite.com
    • @@ -735,9 +734,9 @@ b { display: inline-block; margin-right: 10px; } --- -### Displaying a pending visual state {/*displaying-a-pending-visual-state*/} +### Отображение ожидающего визуального состояния {/*displaying-a-pending-visual-state*/} -You can use the `isPending` boolean value returned by `useTransition` to indicate to the user that a Transition is in progress. For example, the tab button can have a special "pending" visual state: +Вы можете использовать булево значение `isPending`, возвращаемое `useTransition`, чтобы указать пользователю, что происходит Переход. Например, кнопка вкладки может иметь специальное визуальное состояние «ожидание»: ```js {4-6} function TabButton({ action, children, isActive }) { @@ -749,7 +748,7 @@ function TabButton({ action, children, isActive }) { // ... ``` -Notice how clicking "Posts" now feels more responsive because the tab button itself updates right away: +Обратите внимание, что нажатие на «Публикации» теперь кажется более отзывчивым, потому что кнопка вкладки сразу же обновляется: @@ -768,19 +767,19 @@ export default function TabContainer() { isActive={tab === 'about'} action={() => setTab('about')} > - About + Обо мне setTab('posts')} > - Posts (slow) + Публикации (замедлена) setTab('contact')} > - Contact + Контакты
      {tab === 'about' && } @@ -817,7 +816,7 @@ export default function TabButton({ action, children, isActive }) { ```js src/AboutTab.js export default function AboutTab() { return ( -

      Welcome to my profile!

      +

      Добро пожаловать в мой профиль!

      ); } ``` @@ -826,8 +825,8 @@ export default function AboutTab() { import { memo } from 'react'; const PostsTab = memo(function PostsTab() { - // Log once. The actual slowdown is inside SlowPost. - console.log('[ARTIFICIALLY SLOW] Rendering 500 '); + // Логировать один раз. Фактическое замедление происходит внутри SlowPost. + console.log('[ИСКУССТВЕННО ЗАМЕДЛЕННО] Рендеринг 500 '); let items = []; for (let i = 0; i < 500; i++) { @@ -843,12 +842,12 @@ const PostsTab = memo(function PostsTab() { function SlowPost({ index }) { let startTime = performance.now(); while (performance.now() - startTime < 1) { - // Do nothing for 1 ms per item to emulate extremely slow code + // Ничего не делать в течение 1 мс за элемент, чтобы эмулировать чрезвычайно медленный код } return (
    • - Post #{index + 1} + Публикация №{index + 1}
    • ); } @@ -861,7 +860,7 @@ export default function ContactTab() { return ( <>

      - You can find me online here: + Найти меня в Интернете можно здесь:

      • admin@mysite.com
      • @@ -882,9 +881,9 @@ b { display: inline-block; margin-right: 10px; } --- -### Preventing unwanted loading indicators {/*preventing-unwanted-loading-indicators*/} +### Предотвращение нежелательных индикаторов загрузки {/*preventing-unwanted-loading-indicators*/} -In this example, the `PostsTab` component fetches some data using [use](/reference/react/use). When you click the "Posts" tab, the `PostsTab` component *suspends*, causing the closest loading fallback to appear: +В этом примере компонент `PostsTab` получает некоторые данные, используя [use](/reference/react/use). Когда вы нажимаете на вкладку «Публикации», компонент `PostsTab` *задерживается*, что приводит к появлению ближайшего запасного варианта загрузки: @@ -898,24 +897,24 @@ import ContactTab from './ContactTab.js'; export default function TabContainer() { const [tab, setTab] = useState('about'); return ( - 🌀 Loading...}> + 🌀 Загрузка...}> setTab('about')} > - About + Обо мне setTab('posts')} > - Posts + Публикации setTab('contact')} > - Contact + Контакты
        {tab === 'about' && } @@ -944,7 +943,7 @@ export default function TabButton({ action, children, isActive }) { ```js src/AboutTab.js hidden export default function AboutTab() { return ( -

        Welcome to my profile!

        +

        Добро пожаловать в мой профиль!

        ); } ``` @@ -980,7 +979,7 @@ export default function ContactTab() { return ( <>

        - You can find me online here: + Найти меня в Интернете можно здесь:

        • admin@mysite.com
        • @@ -993,9 +992,9 @@ export default function ContactTab() { ```js src/data.js hidden -// Note: the way you would do data fetching depends on -// the framework that you use together with Suspense. -// Normally, the caching logic would be inside a framework. +// Примечание: способ получения данных зависит от фреймворка, +// который вы используете вместе с Задержкой. +// Обычно логика кэширования находится внутри фреймворка. let cache = new Map(); @@ -1010,12 +1009,12 @@ async function getData(url) { if (url.startsWith('/posts')) { return await getPosts(); } else { - throw Error('Not implemented'); + throw Error('Не реализовано'); } } async function getPosts() { - // Add a fake delay to make waiting noticeable. + // Добавьте фиктивную задержку, чтобы ожидание было заметным. await new Promise(resolve => { setTimeout(resolve, 1000); }); @@ -1023,7 +1022,7 @@ async function getPosts() { for (let i = 0; i < 500; i++) { posts.push({ id: i, - title: 'Post #' + (i + 1) + title: 'Публикация №' + (i + 1) }); } return posts; @@ -1038,9 +1037,9 @@ b { display: inline-block; margin-right: 10px; } -Hiding the entire tab container to show a loading indicator leads to a jarring user experience. If you add `useTransition` to `TabButton`, you can instead display the pending state in the tab button instead. +Скрытие всего контейнера вкладок для отображения индикатора загрузки приводит к неприятному пользовательскому опыту. Если вы добавите `useTransition` в `TabButton`, вы можете вместо этого показать состояние ожидания в кнопке вкладки. -Notice that clicking "Posts" no longer replaces the entire tab container with a spinner: +Обратите внимание, что нажатие на «Публикации» больше не заменяет весь контейнер вкладок на спиннер: @@ -1054,24 +1053,24 @@ import ContactTab from './ContactTab.js'; export default function TabContainer() { const [tab, setTab] = useState('about'); return ( - 🌀 Loading...}> + 🌀 Загрузка...}> setTab('about')} > - About + Обо мне setTab('posts')} > - Posts + Публикации setTab('contact')} > - Contact + Контакты
          {tab === 'about' && } @@ -1108,7 +1107,7 @@ export default function TabButton({ action, children, isActive }) { ```js src/AboutTab.js hidden export default function AboutTab() { return ( -

          Welcome to my profile!

          +

          Добро пожаловать в мой профиль!

          ); } ``` @@ -1144,7 +1143,7 @@ export default function ContactTab() { return ( <>

          - You can find me online here: + Найти меня в Интернете можно здесь:

          • admin@mysite.com
          • @@ -1157,9 +1156,9 @@ export default function ContactTab() { ```js src/data.js hidden -// Note: the way you would do data fetching depends on -// the framework that you use together with Suspense. -// Normally, the caching logic would be inside a framework. +// Примечание: способ получения данных зависит от фреймворка, +// который вы используете вместе с Задержкой. +// Обычно логика кэширования находится внутри фреймворка. let cache = new Map(); @@ -1174,12 +1173,12 @@ async function getData(url) { if (url.startsWith('/posts')) { return await getPosts(); } else { - throw Error('Not implemented'); + throw Error('Не реализовано'); } } async function getPosts() { - // Add a fake delay to make waiting noticeable. + // Добавьте фиктивную задержку, чтобы ожидание было заметным. await new Promise(resolve => { setTimeout(resolve, 1000); }); @@ -1187,7 +1186,7 @@ async function getPosts() { for (let i = 0; i < 500; i++) { posts.push({ id: i, - title: 'Post #' + (i + 1) + title: 'Публикация №' + (i + 1) }); } return posts; @@ -1202,19 +1201,19 @@ b { display: inline-block; margin-right: 10px; } -[Read more about using Transitions with Suspense.](/reference/react/Suspense#preventing-already-revealed-content-from-hiding) +[Узнайте больше об использовании Переходов с Задержкой.](/reference/react/Suspense#preventing-already-revealed-content-from-hiding) -Transitions only "wait" long enough to avoid hiding *already revealed* content (like the tab container). If the Posts tab had a [nested `` boundary,](/reference/react/Suspense#revealing-nested-content-as-it-loads) the Transition would not "wait" for it. +Переходы будут «ждать» достаточно долго, чтобы не скрыть *уже показанный* контент (например, контейнер вкладок). Если бы во вкладке «Публикации» присутствовала [вложенная граница ``](/reference/react/Suspense#revealing-nested-content-as-it-loads), Переход бы её не «ждал». --- -### Building a Suspense-enabled router {/*building-a-suspense-enabled-router*/} +### Создание маршрутизатора, поддерживающего Задержку {/*building-a-suspense-enabled-router*/} -If you're building a React framework or a router, we recommend marking page navigations as Transitions. +Если вы создаёте React-фреймворк или маршрутизатор, мы рекомендуем помечать навигацию между страницами как Переходы. ```js {3,6,8} function Router() { @@ -1229,13 +1228,13 @@ function Router() { // ... ``` -This is recommended for three reasons: +Это рекомендуется по трём причинам: -- [Transitions are interruptible,](#marking-a-state-update-as-a-non-blocking-transition) which lets the user click away without waiting for the re-render to complete. -- [Transitions prevent unwanted loading indicators,](#preventing-unwanted-loading-indicators) which lets the user avoid jarring jumps on navigation. -- [Transitions wait for all pending actions](#perform-non-blocking-updates-with-actions) which lets the user wait for side effects to complete before the new page is shown. +- [Переходы прерываемы,](#marking-a-state-update-as-a-non-blocking-transition) что позволяет пользователю кликнуть куда-то ещё, не дожидаясь завершения повторного рендера. +- [Переходы предотвращают нежелательные индикаторы загрузки,](#preventing-unwanted-loading-indicators) что позволяет пользователю избежать резких скачков при навигации. +- [Переходы ожидают завершения всех ожидающих Действий](#perform-non-blocking-updates-with-actions), позволяя пользователю дождаться выполнения побочных эффектов перед отображением новой страницы. -Here is a simplified router example using Transitions for navigations. +Вот небольшой упрощённый пример маршрутизатора, использующего Переходы для навигации. @@ -1286,7 +1285,7 @@ function Router() { } function BigSpinner() { - return

            🌀 Loading...

            ; + return

            🌀 Загрузка...

            ; } ``` @@ -1297,7 +1296,7 @@ export default function Layout({ children, isPending }) {
            - Music Browser + Браузер музыки
            {children} @@ -1311,7 +1310,7 @@ export default function Layout({ children, isPending }) { export default function IndexPage({ navigate }) { return ( ); } @@ -1391,9 +1390,9 @@ export default function Panel({ children }) { ``` ```js src/data.js hidden -// Note: the way you would do data fetching depends on -// the framework that you use together with Suspense. -// Normally, the caching logic would be inside a framework. +// Примечание: способ получения данных зависит от фреймворка, +// который вы используете вместе с Задержкой. +// Обычно логика кэширования находится внутри фреймворка. let cache = new Map(); @@ -1410,24 +1409,24 @@ async function getData(url) { } else if (url === '/the-beatles/bio') { return await getBio(); } else { - throw Error('Not implemented'); + throw Error('Не реализовано'); } } async function getBio() { - // Add a fake delay to make waiting noticeable. + // Добавьте фиктивную задержку, чтобы ожидание было заметным. await new Promise(resolve => { setTimeout(resolve, 500); }); - return `The Beatles were an English rock band, - formed in Liverpool in 1960, that comprised - John Lennon, Paul McCartney, George Harrison - and Ringo Starr.`; + return `The Beatles — английская рок-группа, + сформированная в Ливерпуле в 1960 году, в состав которой входили + Джон Леннон, Пол Маккартни, Джордж Харрисон + и Ринго Старр.`; } async function getAlbums() { - // Add a fake delay to make waiting noticeable. + // Добавьте фиктивную задержку, чтобы ожидание было заметным. await new Promise(resolve => { setTimeout(resolve, 3000); }); @@ -1536,15 +1535,15 @@ main { -[Suspense-enabled](/reference/react/Suspense) routers are expected to wrap the navigation updates into Transitions by default. +Ожидается, что маршрутизаторы, поддерживающие [Задержку](/reference/react/Suspense), по умолчанию оборачивают обновления навигации в Переходы. --- -### Displaying an error to users with an error boundary {/*displaying-an-error-to-users-with-error-boundary*/} +### Отображение ошибки пользователю с помощью границы ошибок {/*displaying-an-error-to-users-with-error-boundary*/} -If a function passed to `startTransition` throws an error, you can display an error to your user with an [error boundary](/reference/react/Component#catching-rendering-errors-with-an-error-boundary). To use an error boundary, wrap the component where you are calling the `useTransition` in an error boundary. Once the function passed to `startTransition` errors, the fallback for the error boundary will be displayed. +Если функция, переданная в `startTransition`, выбрасывает ошибку, вы можете отобразить её пользователю с помощью [границы ошибок](/reference/react/Component#catching-rendering-errors-with-an-error-boundary). Чтобы использовать границу ошибок, оберните компонент, в котором вызывается `useTransition`, в границу ошибок. Как только функция, переданная в `startTransition`, выдаст ошибку, будет отображён запасной интерфейс от границы ошибок. @@ -1554,16 +1553,16 @@ import { ErrorBoundary } from "react-error-boundary"; export function AddCommentContainer() { return ( - ⚠️Something went wrong

            }> + ⚠️Что-то пошло не так

            }>
            ); } function addComment(comment) { - // For demonstration purposes to show Error Boundary + // Для демонстрации работы границы ошибок if (comment == null) { - throw new Error("Example Error: An error thrown to trigger error boundary"); + throw new Error("Пример ошибки: Искусственно выброшенная ошибка для проверки границы ошибок"); } } @@ -1575,13 +1574,13 @@ function AddCommentButton() { disabled={pending} onClick={() => { startTransition(() => { - // Intentionally not passing a comment - // so error gets thrown + // Специально не передаём комментарий + // чтобы сгенерировать ошибку addComment(); }); }} > - Add comment + Добавить комментарий ); } @@ -1624,17 +1623,17 @@ root.render( --- -## Troubleshooting {/*troubleshooting*/} +## Устранение неполадок {/*troubleshooting*/} -### Updating an input in a Transition doesn't work {/*updating-an-input-in-a-transition-doesnt-work*/} +### Обновление ввода во время Перехода не работает {/*updating-an-input-in-a-transition-doesnt-work*/} -You can't use a Transition for a state variable that controls an input: +Вы не можете использовать Переход для переменной состояния, которая управляет вводом: ```js {4,10} const [text, setText] = useState(''); // ... function handleChange(e) { - // ❌ Can't use Transitions for controlled input state + // ❌ Нельзя использовать Переходы для контролируемого состояния ввода startTransition(() => { setText(e.target.value); }); @@ -1643,41 +1642,41 @@ function handleChange(e) { return ; ``` -This is because Transitions are non-blocking, but updating an input in response to the change event should happen synchronously. If you want to run a Transition in response to typing, you have two options: +Это происходит, потому что Переходы являются неблокирующими, но обновление ввода в ответ на событие изменения должно происходить синхронно. Если вы хотите запустить Переход при вводе текста, у вас есть два варианта: -1. You can declare two separate state variables: one for the input state (which always updates synchronously), and one that you will update in a Transition. This lets you control the input using the synchronous state, and pass the Transition state variable (which will "lag behind" the input) to the rest of your rendering logic. -2. Alternatively, you can have one state variable, and add [`useDeferredValue`](/reference/react/useDeferredValue) which will "lag behind" the real value. It will trigger non-blocking re-renders to "catch up" with the new value automatically. +1. Вы можете объявить две отдельные переменные состояния: одну для состояния ввода (которая всегда обновляется синхронно), и одну, которую вы будете обновлять во время Перехода. Это позволит вам управлять вводом с использованием синхронного состояния и передавать переменную состояния Перехода (которая будет «отставать» от ввода) в остальную логику рендеринга. +2. В качестве альтернативы, вы можете использовать одну переменную состояния и добавить [`useDeferredValue`](/reference/react/useDeferredValue), так что она будет «отставать» от реального значения. Она будет вызывать неблокирующие перерисовки, чтобы «догнать» новое значение автоматически. --- -### React doesn't treat my state update as a Transition {/*react-doesnt-treat-my-state-update-as-a-transition*/} +### React не обрабатывает моё обновление состояния как Переход {/*react-doesnt-treat-my-state-update-as-a-transition*/} -When you wrap a state update in a Transition, make sure that it happens *during* the `startTransition` call: +Когда вы оборачиваете обновление состояния в Переход, убедитесь, что оно происходит *во время* вызова `startTransition`. ```js startTransition(() => { - // ✅ Setting state *during* startTransition call + // ✅ Установка состояния *во время* вызова startTransition setPage('/about'); }); ``` -The function you pass to `startTransition` must be synchronous. You can't mark an update as a Transition like this: +Функция, которую вы передаёте `startTransition`, должна быть синхронной. Вы не можете отметить обновление как Переход вот так: ```js startTransition(() => { - // ❌ Setting state *after* startTransition call + // ❌ Установка состояния *после* вызова startTransition setTimeout(() => { setPage('/about'); }, 1000); }); ``` -Instead, you could do this: +Вместо этого вы можете сделать следующее: ```js setTimeout(() => { startTransition(() => { - // ✅ Setting state *during* startTransition call + // ✅ Установка состояния *во время* вызова startTransition setPage('/about'); }); }, 1000); @@ -1685,43 +1684,43 @@ setTimeout(() => { --- -### React doesn't treat my state update after `await` as a Transition {/*react-doesnt-treat-my-state-update-after-await-as-a-transition*/} +### React не считает обновление состояния после `await` Переходом {/*react-doesnt-treat-my-state-update-after-await-as-a-transition*/} -When you use `await` inside a `startTransition` function, the state updates that happen after the `await` are not marked as Transitions. You must wrap state updates after each `await` in a `startTransition` call: +Когда вы используете `await` внутри функции `startTransition`, обновления состояния, которые происходят после `await`, не помечаются как Переходы. Чтобы исправить это, необходимо обернуть каждое обновление состояния после `await` в отдельный вызов `startTransition`: ```js startTransition(async () => { await someAsyncFunction(); - // ❌ Not using startTransition after await + // ❌ Не используется startTransition после await setPage('/about'); }); ``` -However, this works instead: +Однако, это будет работать вместо этого: ```js startTransition(async () => { await someAsyncFunction(); - // ✅ Using startTransition *after* await + // ✅ Использование startTransition *после* await startTransition(() => { setPage('/about'); }); }); ``` -This is a JavaScript limitation due to React losing the scope of the async context. In the future, when [AsyncContext](https://github.com/tc39/proposal-async-context) is available, this limitation will be removed. +Это ограничение JavaScript, связанное с тем, что React теряет область видимости асинхронного контекста. В будущем, когда станет доступен [AsyncContext](https://github.com/tc39/proposal-async-context), это ограничение будет снято. --- -### I want to call `useTransition` from outside a component {/*i-want-to-call-usetransition-from-outside-a-component*/} +### Я хочу вызвать `useTransition` вне компонента {/*i-want-to-call-usetransition-from-outside-a-component*/} -You can't call `useTransition` outside a component because it's a Hook. In this case, use the standalone [`startTransition`](/reference/react/startTransition) method instead. It works the same way, but it doesn't provide the `isPending` indicator. +Вы не можете вызывать `useTransition` вне компонента, так как это хук. В этом случае, используйте отдельный метод [`startTransition`](/reference/react/startTransition). Он работает так же, но не предоставляет индикатор `isPending`. --- -### The function I pass to `startTransition` executes immediately {/*the-function-i-pass-to-starttransition-executes-immediately*/} +### Функция, которую я передаю `startTransition`, сразу же выполняется {/*the-function-i-pass-to-starttransition-executes-immediately*/} -If you run this code, it will print 1, 2, 3: +Если вы запустите этот код, он напечатает 1, 2, 3: ```js {1,3,6} console.log(1); @@ -1732,10 +1731,10 @@ startTransition(() => { console.log(3); ``` -**It is expected to print 1, 2, 3.** The function you pass to `startTransition` does not get delayed. Unlike with the browser `setTimeout`, it does not run the callback later. React executes your function immediately, but any state updates scheduled *while it is running* are marked as Transitions. You can imagine that it works like this: +**Ожидается, что будет напечатано 1, 2, 3.** Функция, которую вы передаёте `startTransition`, не задерживается. В отличие от `setTimeout` в браузере, она не запускает колбэк позже. React немедленно выполняет вашу функцию, но любые обновления состояния, запланированные *во время её выполнения*, помечаются как Переходы. Можно представить, что это работает так: ```js -// A simplified version of how React works +// Упрощённая версия того, как работает React let isInsideTransition = false; @@ -1747,20 +1746,20 @@ function startTransition(scope) { function setState() { if (isInsideTransition) { - // ... schedule a Transition state update ... + // ... запланировать обновление состояния Перехода ... } else { - // ... schedule an urgent state update ... + // ... запланировать срочное обновление состояния ... } } ``` -### My state updates in Transitions are out of order {/*my-state-updates-in-transitions-are-out-of-order*/} +### Мои обновления состояния в Переходах приходят не по порядку {/*my-state-updates-in-transitions-are-out-of-order*/} -If you `await` inside `startTransition`, you might see the updates happen out of order. +Если вы используете `await` внутри `startTransition`, вы можете столкнуться с тем, что обновления будут происходить не в том порядке. -In this example, the `updateQuantity` function simulates a request to the server to update the item's quantity in the cart. This function *artificially returns the every other request after the previous* to simulate race conditions for network requests. +В этом примере функция `updateQuantity` имитирует запрос к серверу для обновления количества товара в корзине. Эта функция *искусственно возвращает каждый второй запрос после предыдущего*, чтобы смоделировать состояние гонки сетевых запросов. -Try updating the quantity once, then update it quickly multiple times. You might see the incorrect total: +Попробуйте сначала обновить количество один раз, а затем быстро несколько раз подряд. Возможно, вы увидите некорректное итоговое значение: @@ -1788,14 +1787,14 @@ import Total from "./Total"; export default function App({}) { const [quantity, setQuantity] = useState(1); const [isPending, startTransition] = useTransition(); - // Store the actual quantity in separate state to show the mismatch. + // Храним фактическое количество в отдельном состоянии, чтобы показать расхождение. const [clientQuantity, setClientQuantity] = useState(1); const updateQuantityAction = newQuantity => { setClientQuantity(newQuantity); - // Access the pending state of the transition, - // by wrapping in startTransition again. + // Получаем доступ к состоянию ожидания перехода, + // обернув вызов снова в startTransition. startTransition(async () => { const savedQuantity = await updateQuantity(newQuantity); startTransition(() => { @@ -1806,7 +1805,7 @@ export default function App({}) { return (
            -

            Checkout

            +

            Оформление заказа


            @@ -1821,15 +1820,15 @@ import {startTransition} from 'react'; export default function Item({action}) { function handleChange(e) { - // Update the quantity in an Action. + // Обновляем количество внутри Действия. startTransition(() => { action(e.target.value); }); } return (
            - Eras Tour Tickets - + Билеты на концерты группы Era + - Total: + Итого:
            {isPending - ? "🌀 Updating..." + ? "🌀 Обновляется..." : `${intl.format(savedQuantity * 9999)}`}
            {!isPending && clientQuantity !== savedQuantity && - `Wrong total, expected: ${intl.format(clientQuantity * 9999)}`} + `Неверный итог, ожидалось: ${intl.format(clientQuantity * 9999)}`}
            @@ -1877,7 +1876,7 @@ export async function updateQuantity(newName) { setTimeout(() => { firstRequest = true; resolve(newName); - // Simulate every other request being slower + // Имитируем, что каждый второй запрос выполняется медленнее }, 1000); } else { setTimeout(() => { @@ -1928,8 +1927,7 @@ export async function updateQuantity(newName) { -When clicking multiple times, it's possible for previous requests to finish after later requests. When this happens, React currently has no way to know the intended order. This is because the updates are scheduled asynchronously, and React loses context of the order across the async boundary. - -This is expected, because Actions within a Transition do not guarantee execution order. For common use cases, React provides higher-level abstractions like [`useActionState`](/reference/react/useActionState) and [`` actions](/reference/react-dom/components/form) that handle ordering for you. For advanced use cases, you'll need to implement your own queuing and abort logic to handle this. +При множественном нажатии возможно, что предыдущие запросы завершатся после более поздних. В таких случаях React на данный момент не может определить предполагаемый порядок. Это происходит потому, что обновления планируются асинхронно, и React теряет контекст порядка на границе асинхронного кода. +Это ожидаемое поведение, так как Действия внутри одного Перехода не гарантируют порядок выполнения. Для распространённых случаев React предоставляет более высокоуровневые абстракции, такие как [`useActionState`](/reference/react/useActionState) и [`` действия](/reference/react-dom/components/form), которые сами управляют порядком. Для более сложных кейсов придётся реализовать собственную логику очередей и отмены запросов.