|
| 1 | +--- |
| 2 | +title: useEffectEvent |
| 3 | +--- |
| 4 | + |
| 5 | +<Intro> |
| 6 | + |
| 7 | +`useEffectEvent` — это хук React, который позволяет вынести не реактивную (не зависящую от состояния или пропсов) логику из эффекта в переиспользуемую функцию, называемую [Effect Event](/learn/separating-events-from-effects#declaring-an-effect-event). |
| 8 | + |
| 9 | +```js |
| 10 | +const onSomething = useEffectEvent(callback) |
| 11 | +``` |
| 12 | + |
| 13 | +</Intro> |
| 14 | + |
| 15 | +<InlineToc /> |
| 16 | + |
| 17 | +## Справочник {/*reference*/} |
| 18 | + |
| 19 | +### `useEffectEvent(callback)` {/*useeffectevent*/} |
| 20 | + |
| 21 | +Вызывайте `useEffectEvent` на верхнем уровне вашего компонента, чтобы объявить **Effect Event**. |
| 22 | +Effect Event — это функция, которую можно вызывать внутри эффектов (`useEffect`, `useLayoutEffect`, и т.д.): |
| 23 | + |
| 24 | +```js {4-6,11} |
| 25 | +import { useEffectEvent, useEffect } from 'react'; |
| 26 | + |
| 27 | +function ChatRoom({ roomId, theme }) { |
| 28 | + const onConnected = useEffectEvent(() => { |
| 29 | + showNotification('Connected!', theme); |
| 30 | + }); |
| 31 | + |
| 32 | + useEffect(() => { |
| 33 | + const connection = createConnection(serverUrl, roomId); |
| 34 | + connection.on('connected', () => { |
| 35 | + onConnected(); |
| 36 | + }); |
| 37 | + connection.connect(); |
| 38 | + return () => connection.disconnect(); |
| 39 | + }, [roomId]); |
| 40 | + |
| 41 | + // ... |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +[См. другие примеры ниже.](#usage) |
| 46 | + |
| 47 | +#### Параметры {/*parameters*/} |
| 48 | + |
| 49 | +* `callback`: функция, содержащая логику вашего Effect Event. |
| 50 | + При определении события через `useEffectEvent`, переданная `callback` всегда получает доступ к **актуальным значениям пропсов и состояния** на момент вызова. |
| 51 | + Это помогает избежать проблем с **устаревшими замыканиями** (stale closures). |
| 52 | + |
| 53 | +#### Возвращаемое значение {/*returns*/} |
| 54 | + |
| 55 | +Возвращает функцию Effect Event. Эту функцию можно вызывать внутри `useEffect`, `useLayoutEffect` или `useInsertionEffect`. |
| 56 | + |
| 57 | +#### Замечания {/*caveats*/} |
| 58 | + |
| 59 | +* **Вызывать только внутри эффектов:** |
| 60 | + Effect Events должны вызываться *только* внутри эффектов. |
| 61 | + Определяйте их непосредственно перед эффектом, который их использует. |
| 62 | + Не передавайте их в другие компоненты или хуки. |
| 63 | + Линтер [`eslint-plugin-react-hooks`](/reference/eslint-plugin-react-hooks) (версии 6.1.1 и выше) проверяет это ограничение и не даст вызвать Effect Event в неправильном контексте. |
| 64 | + |
| 65 | +* **Не сокращает зависимости:** |
| 66 | + Не используйте `useEffectEvent` как способ “избежать” добавления зависимостей в массив зависимостей эффекта. |
| 67 | + Это может скрыть ошибки и ухудшить читаемость кода. |
| 68 | + Лучше явно указывать зависимости или использовать `ref`, если нужно сравнивать предыдущие значения. |
| 69 | + |
| 70 | +* **Используется для не реактивной логики:** |
| 71 | + Применяйте `useEffectEvent` только для логики, не зависящей напрямую от изменений состояния или пропсов. |
| 72 | +___ |
| 73 | + |
| 74 | +## Применение {/*usage*/} |
| 75 | + |
| 76 | +### Чтение актуальных пропсов и состояния {/*reading-the-latest-props-and-state*/} |
| 77 | + |
| 78 | +Обычно, когда вы используете реактивные значения (пропсы или состояние) внутри эффекта, |
| 79 | +нужно добавить их в массив зависимостей. |
| 80 | +Это гарантирует, что эффект выполнится снова при изменении этих значений — что чаще всего и нужно. |
| 81 | + |
| 82 | +Но бывают случаи, когда необходимо **читать самые свежие значения пропсов или состояния**, |
| 83 | +не вызывая при этом повторного выполнения эффекта при каждом их изменении. |
| 84 | + |
| 85 | +Чтобы [читать актуальные значения пропсов или состояния](/learn/separating-events-from-effects#reading-latest-props-and-state-with-effect-events) |
| 86 | +внутри эффекта без превращения их в реактивные зависимости, включите их в Effect Event. |
| 87 | + |
| 88 | +```js {7-9,12} |
| 89 | +import { useEffect, useContext, useEffectEvent } from 'react'; |
| 90 | + |
| 91 | +function Page({ url }) { |
| 92 | + const { items } = useContext(ShoppingCartContext); |
| 93 | + const numberOfItems = items.length; |
| 94 | + |
| 95 | + const onNavigate = useEffectEvent((visitedUrl) => { |
| 96 | + logVisit(visitedUrl, numberOfItems); |
| 97 | + }); |
| 98 | + |
| 99 | + useEffect(() => { |
| 100 | + onNavigate(url); |
| 101 | + }, [url]); |
| 102 | + |
| 103 | + // ... |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +В этом примере эффект должен выполняться повторно при изменении `url` (чтобы зафиксировать посещение новой страницы), но **не должен** выполняться заново при изменении `numberOfItems`. Оборачивая логику логирования в Effect Event, мы делаем `numberOfItems` *нереактивным*: оно всегда читается в своём актуальном виде, но не вызывает повторный рендер или перезапуск эффекта. При этом вы можете передавать реактивные значения, такие как `url`, в аргументах Effect Event — чтобы сохранить их реактивность и при этом иметь доступ к свежим нереактивным данным внутри события. |
0 commit comments