From b9e8b26f7f4a5056799dbaec680c2bf29f2ee724 Mon Sep 17 00:00:00 2001 From: Pavel Yakupov Date: Sat, 13 May 2023 00:27:55 +0300 Subject: [PATCH 1/5] =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B2=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20=D0=B1=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queueing-a-series-of-state-updates.md | 174 ++++++++---------- 1 file changed, 80 insertions(+), 94 deletions(-) diff --git a/src/content/learn/queueing-a-series-of-state-updates.md b/src/content/learn/queueing-a-series-of-state-updates.md index a63b7205b..138a8edf0 100644 --- a/src/content/learn/queueing-a-series-of-state-updates.md +++ b/src/content/learn/queueing-a-series-of-state-updates.md @@ -1,23 +1,20 @@ --- -title: Queueing a Series of State Updates +title: Постановка в очередь серии обновления состояний --- - -Setting a state variable will queue another render. But sometimes you might want to perform multiple operations on the value before queueing the next render. To do this, it helps to understand how React batches state updates. - +Обновление переменной состояния запускает в очередь новый рендер. Но иногда вам может понадобиться выполнить множество операций со значением перед следующим рендером. Чтобы сделать это, стоит понять, как React группирует обновления состояния. -* What "batching" is and how React uses it to process multiple state updates -* How to apply several updates to the same state variable in a row +* Что такое "группировка" и как React иcпользует ее для обработки множества обновлений состояния +* Как назначить несколько обновлений к одной и той же переменной состояния подряд -## React batches state updates {/*react-batches-state-updates*/} - -You might expect that clicking the "+3" button will increment the counter three times because it calls `setNumber(number + 1)` three times: +## React группирует обновления состояния {/*react-batches-state-updates*/} +Вы можете ожидать, что нажимая по кнопку "+3", вы увеличите значение счетчика трижды, т.к. `setNumber(number + 1)` вызывается три раза: @@ -47,29 +44,30 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -However, as you might recall from the previous section, [each render's state values are fixed](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), so the value of `number` inside the first render's event handler is always `0`, no matter how many times you call `setNumber(1)`: - +Кроме того, как вы, возможно, помните из прошлой секции, [each render's state values are fixed](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), значение `number` внутри обработчика событий первого рендера всегда равно `0`, независимо от того, сколько раз вы вызвали функцию `setNumber(1)`: ```js setNumber(0 + 1); setNumber(0 + 1); setNumber(0 + 1); ``` +Кроме этого, есть еще один важный фактор. **React будет ждать пока *весь* код во всех обработчиках событий отработает, перед тем как выполнить обновления состояния.** Вот почему ре-рендер происходит *после* всех вызовов `setNumber()`. -But there is one other factor at play here. **React waits until *all* code in the event handlers has run before processing your state updates.** This is why the re-render only happens *after* all these `setNumber()` calls. -This might remind you of a waiter taking an order at the restaurant. A waiter doesn't run to the kitchen at the mention of your first dish! Instead, they let you finish your order, let you make changes to it, and even take orders from other people at the table. +Для примера воспомним официанта, который принимает заказ в ресторане. Официант не бежит на кухню сразу после того, как услышал первое блюдо! Вместо этого, он ждет пока вы закончите свой заказ, уточняет его детали, и даже принимает заказы от других людей за столом. -This lets you update multiple state variables--even from multiple components--without triggering too many [re-renders.](/learn/render-and-commit#re-renders-when-state-updates) But this also means that the UI won't be updated until _after_ your event handler, and any code in it, completes. This behavior, also known as **batching,** makes your React app run much faster. It also avoids dealing with confusing "half-finished" renders where only some of the variables have been updated. +Это позволяет нам обновлять несколько переменных состояния--даже от нескольких компонентов--без вызова слишком большком большого количества [ре-рендеров.](/learn/render-and-commit#re-renders-when-state-updates) Но это также означает, что UI не будет обновлен до _того_, как ваши обработчики событий, и код в них, не исполнится. Это поведение, также известное как **группировка,** позволяет вашему React приложению работать гораздо быстрее. Это также позволяет избегать сбивающих с толку "наполовину законченных" рендеров, где обновились только некоторые переменные. + +**React не группирует по *множеству* преднамеренных событий вроде кликов**-- каждый клик обрабыватывается отдельно. В остальном будьте уверены, что React группирует только тогда, когда это в общем безопасно для выполнения. Это гарантирует, например, что если первый клип по кнопке отключает форму, следующий клик не отправит ее снова. -**React does not batch across *multiple* intentional events like clicks**--each click is handled separately. Rest assured that React only does batching when it's generally safe to do. This ensures that, for example, if the first button click disables a form, the second click would not submit it again. -## Updating the same state multiple times before the next render {/*updating-the-same-state-multiple-times-before-the-next-render*/} +## Обновления одного и того же состояния несколько раз до следующего рендера {/*updating-the-same-state-multiple-times-before-the-next-render*/} -It is an uncommon use case, but if you would like to update the same state variable multiple times before the next render, instead of passing the *next state value* like `setNumber(number + 1)`, you can pass a *function* that calculates the next state based on the previous one in the queue, like `setNumber(n => n + 1)`. It is a way to tell React to "do something with the state value" instead of just replacing it. +Это не такой распространенный вариант использования, но если вы захотите обновить одну и ту же переменную состояния несколько раз до следующего рендера, вместо того чтобы передавать *следующее значение состояния* в виде `setNumber(number + 1)`, вы можете передать *функцию,* которая подсчитывает следующее состояние базируясь на предыдущем в очереди, типа `setNumber(n => n + 1)` +Это возможность сказать React "сделай что-то со значением состояния" вместо того, чтобы просто его заменить. -Try incrementing the counter now: +Попробуем увеличить значение счетчика сейчас: @@ -98,11 +96,10 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } ``` +Здесь, в `n => n + 1` вызывается **обновляющая функция.** Когда вы передаете ее в установшик состояния: -Here, `n => n + 1` is called an **updater function.** When you pass it to a state setter: - -1. React queues this function to be processed after all the other code in the event handler has run. -2. During the next render, React goes through the queue and gives you the final updated state. +1. React назначает эту функцию в очередь, которая выполнится после всего остального кода в обработчике событий. +2. Во время следующего рендера, React запустит очередь и выдаст вам финальное обновление состояния. ```js setNumber(n => n + 1); @@ -110,27 +107,26 @@ setNumber(n => n + 1); setNumber(n => n + 1); ``` -Here's how React works through these lines of code while executing the event handler: +Вот как React обработает сквозь эти строчки кода во время выполнения обработчика событий: -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. +1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет их в очередь +1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет их в очередь +1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет их в очередь -When you call `useState` during the next render, React goes through the queue. The previous `number` state was `0`, so that's what React passes to the first updater function as the `n` argument. Then React takes the return value of your previous updater function and passes it to the next updater as `n`, and so on: +Когда вы вызываете `useState` в следующем рендере, React пропускает эту очередь. Предыдущее состояние `number` было `0`, так что эта функция обновления и пропускает в следующем обновление как `n`, и так далее: -| queued update | `n` | returns | +| запланированное обновление | `n` | возвращает | |--------------|---------|-----| | `n => n + 1` | `0` | `0 + 1 = 1` | | `n => n + 1` | `1` | `1 + 1 = 2` | | `n => n + 1` | `2` | `2 + 1 = 3` | -React stores `3` as the final result and returns it from `useState`. - -This is why clicking "+3" in the above example correctly increments the value by 3. -### What happens if you update state after replacing it {/*what-happens-if-you-update-state-after-replacing-it*/} +React сохранит `3` как финальный результат и вернет его из `useState`. +Вот почему нажатие на "+3" в примере выше корректно увеличивает значение на 3. -What about this event handler? What do you think `number` will be in the next render? +### Что случится если вы обновите состояние после его замены {/*what-happens-if-you-update-state-after-replacing-it*/} +Что насчет обработчика событий? Как вы полагаете, какое значение примет `number` в следующем рендере? ```js + }}>Увеличить число ) } @@ -161,7 +161,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Вот что обработчик событий говорит говорит сделать React: +Вот что обработчик событий говорит сделать React: 1. `setNumber(number + 5)`: `number` равен `0`, так что `setNumber(0 + 5)`. React добавит *"заменить с `5`"* в свою очередь. 2. `setNumber(n => n + 1)`: `n => n + 1` это обновляющая функция. React добавит *эту функцию* в свою очередь. @@ -172,7 +172,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } | "заменить с `5`" | `0` (не используется) | `5` | | `n => n + 1` | `5` | `5 + 1 = 6` | -React хранит `6` как финальный результат и возвращает это из `useState`. +React сохранит `6` как финальный результат и вернет его из `useState`. Вы могли заметить что `setState(5)` на самом деле работает как `setState(n => 5)`, но `n` не используется! @@ -204,7 +204,7 @@ export default function Counter() { setNumber(number + 5); setNumber(n => n + 1); setNumber(42); - }}>Increase the number + }}>Увеличить число ) } @@ -218,7 +218,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } Вот как React обработает этот код во время выполнения обработчиков событий: -1. `setNumber(number + 5)`: `number` is `0`, so `setNumber(0 + 5)`. React добавит *"заменить на `5`"* в свою очередь. +1. `setNumber(number + 5)`: `number` равен `0`, так что `setNumber(0 + 5)`. React добавит *"заменить на `5`"* в свою очередь. 2. `setNumber(n => n + 1)`: `n => n + 1` это функция обновления. React добавит *эту функцию* в свою очередь. 3. `setNumber(42)`: React добавит *"заменить на `42`"* в очередь. @@ -236,20 +236,20 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } Подводя итог, вот как вы можете передавать установщик состояния в `setNumber`: * **Обновляющая функцию** (напр. `n => n + 1`) добавляется в очередь. -* **Любое другое значение** (напр. number `5`) добавляет "заменить на `5`" в очередь, игнорируя то, что уже было запланировано. +* **Любое другое значение** (напр. число `5`) добавляет "заменить на `5`" в очередь, игнорируя то, что уже было запланировано. -После того как все обработчики событий закончатся, React запустит ре-рендер. Во время ре-рендера, React обработает очередь. Обновляющие функции выполняются во время рендеринга, так что **так что обновляющие функции должны быть [чистыми](/learn/keeping-components-pure)** и *возвращать* только результат. Не пытайтесь устанавливать состояние изнутри или запускать другие побочные эффекты. -### Соглашение о наименовании {/*naming-conventions*/} +После того как все обработчики событий закончатся, React запустит ре-рендер. Во время ре-рендера, React обработает очередь. Обновляющие функции выполняются во время рендеринга, так что **так что они должны быть [чистыми](/learn/keeping-components-pure)** и *возвращать* только результат. Не пытайтесь устанавливать состояние изнутри или запускать другие побочные эффекты. +### Соглашения об именах {/*naming-conventions*/} -Достаточно распостраненный вариант именования аргумента обновляющей функции первыми буквами связанной с ней переменной состояния: +Достаточно часто аргумент обновляющей функции обозначают первыми буквами связанной с ней переменной состояния: ```js setEnabled(e => !e); setLastName(ln => ln.reverse()); setFriendCount(fc => fc * 2); ``` -Если вы предпочитаете более длинный вариант, другим распространенным соглашенением является повторение полного наименования переменной состояния, типа `setEnabled(enabled => !enabled)`, или же использование префикса типа `setEnabled(prevEnabled => !prevEnabled)`. +Если вы предпочитаете более многословный вариант, другим распространенным соглашенением является повторение полного наименования переменной состояния, типа `setEnabled(enabled => !enabled)`, или же использование префикса типа `setEnabled(prevEnabled => !prevEnabled)`. * Установка состояния не изменяет переменную в текущем рендере, но запустит новый рендер. @@ -263,9 +263,9 @@ setFriendCount(fc => fc * 2); #### Исправление счетчика запросов {/*fix-a-request-counter*/} -Вы работаете над интернет-магазином произведений искусства, в котором пользователю можно делать множество заказов предметов искусства за один раз. Каждый раз когда пользователь нажимает кнопку "Buy", счетчик "Pending" должен увеличиться на единицу. После 3 секунд, "Pending" счетчик должен уменьшаться, а счетчик "Completed" должен увеличиваться. +Вы работаете над интернет-магазином произведений искусства, в котором пользователю можно делать множество заказов предметов искусства за один раз. Каждый раз когда пользователь нажимает кнопку "Купить", счетчик "Ожидание" должен увеличиться на единицу. После 3 секунд, "Ожидание" счетчик должен уменьшаться, а счетчик "Выполнено" должен увеличиваться. -Однако, счетчик "Pending" не работает как задумано. Когда вы нажимаете "Buy", значение уменьшается до `-1`(чего вообще не должно быть). И если вы быстро кликните дважды, оба счетчика сработают непредсказуемо. +Однако, счетчик "Ожидание" не работает как задумано. Когда вы нажимаете "Купить", значение уменьшается до `-1`(чего вообще не должно быть). И если вы быстро кликните дважды, оба счетчика сработают непредсказуемо. Почему это происходит? Давайте починим оба счетчика. @@ -288,13 +288,13 @@ export default function RequestTracker() { return ( <>

- Pending: {pending} + Ожидание: {pending}

- Completed: {completed} + Выполнено: {completed}

); @@ -311,7 +311,7 @@ function delay(ms) { -Внутри обработчика событий `handleClick`, значения `pending` и `completed` связаны с тем что они из себя предствляли на момент клика. В первом рендере, `pending` значение было `0`, так что `setPending(pending - 1)` стал `setPending(-1)`, что неверно. Раз уж мы хотим *увеличивать* или *уменьшать* счетчики, вместо того чтобы устанавливать для них конкретное значение, определенное во время клика, вы можете вместо этого передать функцию обновления: +Внутри обработчика событий `handleClick`, значения `ожидание` и `выполнено` связаны с тем что они из себя предствляли на момент клика. В первом рендере, значение `ожидание` было `0`, так что `setPending(pending - 1)` стал `setPending(-1)`, что неверно. Раз уж мы хотим *увеличивать* или *уменьшать* счетчики, вместо того чтобы устанавливать для них конкретное значение, определенное во время клика, вы можете вместо этого передать функцию обновления: ```js @@ -331,13 +331,13 @@ export default function RequestTracker() { return ( <>

- Pending: {pending} + Ожидание: {pending}

- Completed: {completed} + Выполнено: {completed}

); @@ -459,9 +459,9 @@ function TestCase({ const actual = getFinalState(baseState, queue); return ( <> -

Base state: {baseState}

-

Queue: [{queue.join(', ')}]

-

Expected result: {expected}

+

Изначальное значение: {baseState}

+

Очередь: [{queue.join(', ')}]

+

Ожидаемый результат: {expected}

-

Base state: {baseState}

-

Queue: [{queue.join(', ')}]

-

Expected result: {expected}

+

Изначальное значение: {baseState}

+

Очередь: [{queue.join(', ')}]

+

Ожидаемый результат: {expected}

Date: Sat, 29 Jul 2023 20:43:56 +0300 Subject: [PATCH 3/5] update with fixes --- .../queueing-a-series-of-state-updates.md | 67 +++++++++---------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/src/content/learn/queueing-a-series-of-state-updates.md b/src/content/learn/queueing-a-series-of-state-updates.md index 92c5fad59..1ae240d1c 100644 --- a/src/content/learn/queueing-a-series-of-state-updates.md +++ b/src/content/learn/queueing-a-series-of-state-updates.md @@ -3,18 +3,18 @@ title: Постановка в очередь серии обновления с --- -Обновление переменной состояния запускает в очередь новый рендер. Но иногда вам может понадобиться выполнить множество операций со значением перед следующим рендером. Чтобы сделать это, стоит понять, как React группирует обновления состояния. +Обновление переменной состояния добавляет в очередь новый рендер. Но иногда вам нужно выполнить множество операций со значением перед следующим рендером. Чтобы сделать это, стоит понять, как React группирует обновления состояния. -* Что такое "группировка" и как React иcпользует ее для обработки множества обновлений состояния +* Что такое "группировка" и как React иcпользует её для обработки множества обновлений состояния * Как назначить несколько обновлений к одной и той же переменной состояния подряд ## React группирует обновления состояния {/*react-batches-state-updates*/} -Вы можете ожидать, что нажимая по кнопку "+3", вы увеличите значение счетчика трижды, т.к. `setNumber(number + 1)` вызывается три раза: +Вы можете ожидать, что нажимая на кнопку "+3", вы увеличите значение счетчика трижды, т.к. `setNumber(number + 1)` вызывается три раза: @@ -44,27 +44,27 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Кроме того, как вы, возможно, помните из прошлой секции, [each render's state values are fixed](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), значение `number` внутри обработчика событий первого рендера всегда равно `0`, независимо от того, сколько раз вы вызвали функцию `setNumber(1)`: +Кроме того, как вы, возможно, помните из прошлой секции, [каждое состояние стейта неизменяемо](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), значение `number` внутри обработчика событий первого рендера всегда равно `0`, независимо от того, сколько раз вы вызвали функцию `setNumber(1)`: ```js setNumber(0 + 1); setNumber(0 + 1); setNumber(0 + 1); ``` -Кроме этого, есть еще один важный фактор. **React будет ждать пока *весь* код во всех обработчиках событий отработает, перед тем как выполнить обновления состояния.** Вот почему ре-рендер происходит только *после* всех вызовов `setNumber()`. +Кроме этого, есть еще один важный фактор. **React будет ждать, пока *весь* код во всех обработчиках событий отработает, перед тем как выполнить обновление состояния.** Вот почему ре-рендер происходит только *после* всех вызовов `setNumber()`. -Для примера воспомним официанта, который принимает заказ в ресторане. Официант не бежит на кухню сразу после того, как услышал первое блюдо! Вместо этого, он ждет пока вы закончите свой заказ, уточняет его детали, и даже принимает заказы от других людей за столом. +Для примера вспомним официанта, который принимает заказ в ресторане. Официант не бежит на кухню сразу после того, как услышал первое блюдо! Вместо этого он ждет пока вы закончите свой заказ, уточняет его детали, и даже принимает заказы от других людей за столом. -Это позволяет нам обновлять несколько переменных состояния--даже от нескольких компонентов--без вызова слишком большком большого количества [ре-рендеров.](/learn/render-and-commit#re-renders-when-state-updates) Но это также означает, что UI не будет обновлен до _того_, как ваши обработчики событий, и код в них, не исполнится. Это поведение, также известное как **группировка,** позволяет вашему React приложению работать гораздо быстрее. Это также позволяет избегать сбивающих с толку "наполовину законченных" рендеров, где обновились только некоторые переменные. +Это позволяет нам обновлять несколько переменных состояния--даже от нескольких компонентов--без вызова слишком большого количества [ре-рендеров.](/learn/render-and-commit#re-renders-when-state-updates) Но это также означает, что UI не будет обновлен до _того_, как ваши обработчики событий, и код в них, не исполнится. Это поведение, также известное как **группировка,** позволяет вашему React-приложению работать гораздо быстрее. Это также позволяет избегать сбивающих с толку "наполовину законченных" рендеров, где обновились только некоторые переменные. **React не группирует по *множеству* преднамеренных событий вроде кликов**-- каждый клик обрабыватывается отдельно. В остальном будьте уверены, что React группирует только тогда, когда это в общем безопасно для выполнения. Это гарантирует, например, что если первый клип по кнопке отключает форму, следующий клик не отправит ее снова. ## Обновления одного и того же состояния несколько раз до следующего рендера {/*updating-the-same-state-multiple-times-before-the-next-render*/} -Это не такой распространенный вариант использования, но если вы захотите обновить одну и ту же переменную состояния несколько раз до следующего рендера, вместо того чтобы передавать *следующее значение состояния* в виде `setNumber(number + 1)`, вы можете передать *функцию,* которая подсчитывает следующее состояние базируясь на предыдущем в очереди, типа `setNumber(n => n + 1)`. +Это не такой распространенный вариант использования, но если вы захотите обновить одну и ту же переменную состояния несколько раз до следующего рендера, вместо того чтобы передавать *следующее значение состояния* в виде `setNumber(number + 1)`, вы можете передать *функцию,* которая подсчитывает следующее состояние, базируясь на предыдущем в очереди, типа `setNumber(n => n + 1)`. Это возможность сказать React "сделай что-то со значением состояния" вместо того, чтобы просто его заменить. Попробуем увеличить значение счетчика сейчас: @@ -99,7 +99,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } Здесь, в `n => n + 1` вызывается **обновляющая функция.** Когда вы передаете ее в установщик состояния: 1. React назначает эту функцию в очередь, которая выполнится после всего остального кода в обработчике событий. -2. Во время следующего рендера, React запустит очередь и выдаст вам финальное обновление состояния. +2. Во время следующего рендера React запустит очередь и выдаст вам финальное обновление состояния. ```js setNumber(n => n + 1); @@ -107,13 +107,13 @@ setNumber(n => n + 1); setNumber(n => n + 1); ``` -Вот как React обработает сквозь эти строчки кода во время выполнения обработчика событий: +Вот как React обработает эти строчки кода во время выполнения обработчика событий: -1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет их в очередь -1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет их в очередь -1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет их в очередь +1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет её в очередь +1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет её в очередь +1. `setNumber(n => n + 1)`: `n => n + 1` это функция. React добавляет её в очередь -Когда вы вызываете `useState` в следующем рендере, React пропускает эту очередь. Предыдущее состояние `number` было `0`, так что эта функция обновления и пропускает в следующем обновление как `n`, и так далее: +Когда вы вызываете `useState` в следующем рендере, React проходит через эту очередь. Предыдущее состояние `number` было `0`, так что функция принимает именно это значение в следующем обновлении как `n`, и так далее: | запланированное обновление | `n` | возвращает | |--------------|---------|-----| @@ -162,14 +162,14 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } Вот что обработчик событий говорит сделать React: -1. `setNumber(number + 5)`: `number` равен `0`, так что `setNumber(0 + 5)`. React добавит *"заменить с `5`"* в свою очередь. +1. `setNumber(number + 5)`: `number` равен `0`, так что `setNumber(0 + 5)`. React добавит *"заменить на `5`"* в свою очередь. 2. `setNumber(n => n + 1)`: `n => n + 1` это обновляющая функция. React добавит *эту функцию* в свою очередь. -В продолжении следующего рендера, React пройдет через следующую очередь состояний: +В следующем рендере React пройдет через следующую очередь состояний: | очередь обновлений | `n` | возвращает | |--------------|---------|-----| -| "заменить с `5`" | `0` (не используется) | `5` | +| "заменить на `5`" | `0` (не используется) | `5` | | `n => n + 1` | `5` | `5 + 1 = 6` | React сохранит `6` как финальный результат и вернет его из `useState`. @@ -223,23 +223,22 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } 3. `setNumber(42)`: React добавит *"заменить на `42`"* в очередь. -В продолжении следующего рендера, React пройдет через следующие обновления состояния: +В следующем рендере React пройдет через следующие обновления состояния: | очередь обновления | `n` | возвращает | |--------------|---------|-----| -| "заменить с `5`" | `0` (не используется) | `5` | +| "заменить на `5`" | `0` (не используется) | `5` | | `n => n + 1` | `5` | `5 + 1 = 6` | -| "заменить с `42`" | `6` (не используется) | `42` | +| "заменить на `42`" | `6` (не используется) | `42` | После React сохранит `42` как финальный результат и вернет его из `useState`. -Подводя итог, вот как вы можете передавать установщик состояния в `setNumber`: -* **Обновляющая функцию** (напр. `n => n + 1`) добавляется в очередь. -* **Любое другое значение** (напр. число `5`) добавляет "заменить на `5`" в очередь, игнорируя то, что уже было запланировано. +Подводя итог, вот что вы можете передавать в установщик состояния `setNumber`: +* **Обновляющую функцию** (напр. `n => n + 1`), которая добавляется в очередь. +* **Любое другое значение** (напр. число `5`), которое добавлит "заменить на `5`" в очередь, игнорируя то, что уже было запланировано. - -После того как все обработчики событий закончатся, React запустит ре-рендер. Во время ре-рендера, React обработает очередь. Обновляющие функции выполняются во время рендеринга, так что **так что они должны быть [чистыми](/learn/keeping-components-pure)** и *возвращать* только результат. Не пытайтесь устанавливать состояние изнутри или запускать другие побочные эффекты. +После того как все обработчики событий закончатся, React запустит ре-рендер. Во время ре-рендера React обработает очередь. Обновляющие функции выполняются во время рендеринга, так что **они должны быть [чистыми](/learn/keeping-components-pure)** и *возвращать* только результат. Не пытайтесь устанавливать состояние изнутри или запускать другие побочные эффекты. ### Соглашения об именах {/*naming-conventions*/} Достаточно часто аргумент обновляющей функции обозначают первыми буквами связанной с ней переменной состояния: @@ -252,8 +251,8 @@ setFriendCount(fc => fc * 2); Если вы предпочитаете более многословный вариант, другим распространенным соглашенением является повторение полного наименования переменной состояния, типа `setEnabled(enabled => !enabled)`, или же использование префикса типа `setEnabled(prevEnabled => !prevEnabled)`. -* Установка состояния не изменяет переменную в текущем рендере, но запустит новый рендер. -* React выполнит обновления стейта после обработчиков событий который успешно выполнились. Это называется группировка. +* Установка состояния не изменяет переменную в текущем рендере, но запрашивает новый рендер. +* React выполнит обновления состояния после того, как обработчики событий успешно выполнились. Это называется группировка. * Чтобы обновить любое состояние множество раз за одно событие, можно использовать функцию обновления `setNumber(n => n + 1)`. @@ -263,7 +262,7 @@ setFriendCount(fc => fc * 2); #### Исправление счетчика запросов {/*fix-a-request-counter*/} -Вы работаете над интернет-магазином произведений искусства, в котором пользователю можно делать множество заказов предметов искусства за один раз. Каждый раз когда пользователь нажимает кнопку "Купить", счетчик "Ожидание" должен увеличиться на единицу. После 3 секунд, "Ожидание" счетчик должен уменьшаться, а счетчик "Выполнено" должен увеличиваться. +Вы работаете над интернет-магазином произведений искусства, в котором пользователю можно делать множество заказов предметов искусства за один раз. Каждый раз когда пользователь нажимает кнопку "Купить", счетчик "Ожидание" должен увеличиться на единицу. После 3 секунд, счётчик "Ожидание" должен уменьшаться, а счетчик "Выполнено" должен увеличиваться. Однако, счетчик "Ожидание" не работает как задумано. Когда вы нажимаете "Купить", значение уменьшается до `-1`(чего вообще не должно быть). И если вы быстро кликните дважды, оба счетчика сработают непредсказуемо. @@ -311,7 +310,7 @@ function delay(ms) { -Внутри обработчика событий `handleClick`, значения `ожидание` и `выполнено` связаны с тем что они из себя предствляли на момент клика. В первом рендере, значение `ожидание` было `0`, так что `setPending(pending - 1)` стал `setPending(-1)`, что неверно. Раз уж мы хотим *увеличивать* или *уменьшать* счетчики, вместо того чтобы устанавливать для них конкретное значение, определенное во время клика, вы можете вместо этого передать функцию обновления: +Внутри обработчика событий `handleClick`, значения `pending` и `completed` связаны с тем, что они из себя предствляли на момент клика. В первом рендере значение `pending` было `0`, так что `setPending(pending - 1)` стал `setPending(-1)`, что неверно. Раз уж мы хотим *увеличивать* или *уменьшать* счетчики, вместо того чтобы устанавливать для них конкретное значение, определенное во время клика, вы можете вместо этого передать функцию обновления: ```js @@ -352,23 +351,23 @@ function delay(ms) { -Теперь вы уверенны, что когда вы увеличиваете или уменьшаете счетчик, вы действительно связываете его с *последним* состоянием, а не с тем, каким оно было на момент щелчка. +Теперь вы уверены, что когда вы увеличиваете или уменьшаете счётчик, вы действительно связываете его с *последним* состоянием, а не с тем, каким оно было на момент щелчка. #### Реализуйте очередь состояния самостоятельно {/*implement-the-state-queue-yourself*/} -В этом задании, вы сами создадите крохотную часть React с нуля! Это не так сложно как кажется. +В этом задании вы сами создадите крохотную часть React с нуля! Это не так сложно как кажется. -Посмотрите на превью sandbox. Стоит заметить что тут показано **четыре тестовых примера.** Они связаны с теми примерами, которые вы видели ранее на этой странице. Вашим заданием будет воссоздать функцию `getFinalState` так чтобы корректный результат возвращался в каждом примере. Если вы воссоздадите все правильно, все четыре теста должны пройти. +Посмотрите на превью песочницы. Стоит заметить, что тут показано **четыре тестовых примера.** Они связаны с теми примерами, которые вы видели ранее на этой странице. Вашим заданием будет воссоздать функцию `getFinalState` так чтобы корректный результат возвращался в каждом примере. Если вы воссоздадите все правильно, все четыре теста должны пройти. -Вы получите два аргумента: `baseState` как изначальное состонияние(like `0`), и `очередь`, которая является массивом, который содержит как числа(типа `5`) и обновляющие функции (типа `n => n + 1`) в том порядке в котором они были добавлены. +Вы получите два аргумента: `baseState` как изначальное состонияние(like `0`), и `очередь`, которая является массивом, который содержит как числа(типа `5`), так и обновляющие функции (типа `n => n + 1`) в том порядке, в котором они были добавлены. Ваша задача вернуть итоговое значение, точно так же, как показано в таблицах на этой странице -Если вы чувствуете что застряли, начните вот с такой структуры кода: +Если вы чувствуете, что застряли, начните вот с такой структуры кода: ```js export function getFinalState(baseState, queue) { From b1b42067bef501cd08a1b2a94936652116b91f7c Mon Sep 17 00:00:00 2001 From: Pavel Yakupov Date: Sat, 29 Jul 2023 21:39:55 +0300 Subject: [PATCH 4/5] fix lines --- .../learn/queueing-a-series-of-state-updates.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/content/learn/queueing-a-series-of-state-updates.md b/src/content/learn/queueing-a-series-of-state-updates.md index 1ae240d1c..5cf6ac225 100644 --- a/src/content/learn/queueing-a-series-of-state-updates.md +++ b/src/content/learn/queueing-a-series-of-state-updates.md @@ -4,8 +4,10 @@ title: Постановка в очередь серии обновления с Обновление переменной состояния добавляет в очередь новый рендер. Но иногда вам нужно выполнить множество операций со значением перед следующим рендером. Чтобы сделать это, стоит понять, как React группирует обновления состояния. + + * Что такое "группировка" и как React иcпользует её для обработки множества обновлений состояния @@ -50,6 +52,7 @@ setNumber(0 + 1); setNumber(0 + 1); setNumber(0 + 1); ``` + Кроме этого, есть еще один важный фактор. **React будет ждать, пока *весь* код во всех обработчиках событий отработает, перед тем как выполнить обновление состояния.** Вот почему ре-рендер происходит только *после* всех вызовов `setNumber()`. @@ -96,6 +99,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } ``` + Здесь, в `n => n + 1` вызывается **обновляющая функция.** Когда вы передаете ее в установщик состояния: 1. React назначает эту функцию в очередь, которая выполнится после всего остального кода в обработчике событий. @@ -122,8 +126,8 @@ setNumber(n => n + 1); | `n => n + 1` | `2` | `2 + 1 = 3` | React сохранит `3` как финальный результат и вернет его из `useState`. -Вот почему нажатие на "+3" в примере выше корректно увеличивает значение на 3. +Вот почему нажатие на "+3" в примере выше корректно увеличивает значение на 3. ### Что случится, если вы обновите состояние после его замены {/*what-happens-if-you-update-state-after-replacing-it*/} Что насчет обработчика событий? Как вы полагаете, какое значение примет `number` в следующем рендере? @@ -162,6 +166,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } Вот что обработчик событий говорит сделать React: + 1. `setNumber(number + 5)`: `number` равен `0`, так что `setNumber(0 + 5)`. React добавит *"заменить на `5`"* в свою очередь. 2. `setNumber(n => n + 1)`: `n => n + 1` это обновляющая функция. React добавит *эту функцию* в свою очередь. @@ -218,11 +223,11 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } Вот как React обработает этот код во время выполнения обработчиков событий: + 1. `setNumber(number + 5)`: `number` равен `0`, так что `setNumber(0 + 5)`. React добавит *"заменить на `5`"* в свою очередь. 2. `setNumber(n => n + 1)`: `n => n + 1` это функция обновления. React добавит *эту функцию* в свою очередь. 3. `setNumber(42)`: React добавит *"заменить на `42`"* в очередь. - В следующем рендере React пройдет через следующие обновления состояния: | очередь обновления | `n` | возвращает | @@ -233,12 +238,13 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } После React сохранит `42` как финальный результат и вернет его из `useState`. - Подводя итог, вот что вы можете передавать в установщик состояния `setNumber`: + * **Обновляющую функцию** (напр. `n => n + 1`), которая добавляется в очередь. * **Любое другое значение** (напр. число `5`), которое добавлит "заменить на `5`" в очередь, игнорируя то, что уже было запланировано. -После того как все обработчики событий закончатся, React запустит ре-рендер. Во время ре-рендера React обработает очередь. Обновляющие функции выполняются во время рендеринга, так что **они должны быть [чистыми](/learn/keeping-components-pure)** и *возвращать* только результат. Не пытайтесь устанавливать состояние изнутри или запускать другие побочные эффекты. +После того как все обработчики событий закончатся, React запустит ре-рендер. Во время ре-рендера React обработает очередь. Обновляющие функции выполняются во время рендеринга, так что **они должны быть [чистыми](/learn/keeping-components-pure)** и *возвращать* только результат. Не пытайтесь устанавливать состояние изнутри или запускать другие побочные эффекты. В строгом режиме React будет запускать каждую обновляющую функцию дважды (но не учитывать второй результат), чтобы помочь вам найти ошибки. + ### Соглашения об именах {/*naming-conventions*/} Достаточно часто аргумент обновляющей функции обозначают первыми буквами связанной с ней переменной состояния: @@ -264,6 +270,7 @@ setFriendCount(fc => fc * 2); #### Исправление счетчика запросов {/*fix-a-request-counter*/} Вы работаете над интернет-магазином произведений искусства, в котором пользователю можно делать множество заказов предметов искусства за один раз. Каждый раз когда пользователь нажимает кнопку "Купить", счетчик "Ожидание" должен увеличиться на единицу. После 3 секунд, счётчик "Ожидание" должен уменьшаться, а счетчик "Выполнено" должен увеличиваться. + Однако, счетчик "Ожидание" не работает как задумано. Когда вы нажимаете "Купить", значение уменьшается до `-1`(чего вообще не должно быть). И если вы быстро кликните дважды, оба счетчика сработают непредсказуемо. Почему это происходит? Давайте починим оба счетчика. From c3d3dc426b18358ab85515c7d7078d25dfd132a7 Mon Sep 17 00:00:00 2001 From: Pavel Yakupov Date: Sun, 6 Aug 2023 21:36:02 +0300 Subject: [PATCH 5/5] small fixes --- src/content/learn/queueing-a-series-of-state-updates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/learn/queueing-a-series-of-state-updates.md b/src/content/learn/queueing-a-series-of-state-updates.md index 5cf6ac225..a532052d4 100644 --- a/src/content/learn/queueing-a-series-of-state-updates.md +++ b/src/content/learn/queueing-a-series-of-state-updates.md @@ -46,7 +46,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Кроме того, как вы, возможно, помните из прошлой секции, [каждое состояние стейта неизменяемо](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), значение `number` внутри обработчика событий первого рендера всегда равно `0`, независимо от того, сколько раз вы вызвали функцию `setNumber(1)`: +Кроме того, как вы, возможно, помните из прошлой секции, [значения состояния фиксированы во время рендера](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), значение `number` внутри обработчика событий первого рендера всегда равно `0`, независимо от того, сколько раз вы вызвали функцию `setNumber(1)`: ```js setNumber(0 + 1); setNumber(0 + 1); @@ -241,7 +241,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } Подводя итог, вот что вы можете передавать в установщик состояния `setNumber`: * **Обновляющую функцию** (напр. `n => n + 1`), которая добавляется в очередь. -* **Любое другое значение** (напр. число `5`), которое добавлит "заменить на `5`" в очередь, игнорируя то, что уже было запланировано. +* **Любое другое значение** (напр. число `5`), которое добавит "заменить на `5`" в очередь, игнорируя то, что уже было запланировано. После того как все обработчики событий закончатся, React запустит ре-рендер. Во время ре-рендера React обработает очередь. Обновляющие функции выполняются во время рендеринга, так что **они должны быть [чистыми](/learn/keeping-components-pure)** и *возвращать* только результат. Не пытайтесь устанавливать состояние изнутри или запускать другие побочные эффекты. В строгом режиме React будет запускать каждую обновляющую функцию дважды (но не учитывать второй результат), чтобы помочь вам найти ошибки.