You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
*[Могу ли я пропустить эффект при обновлениях?](#can-i-skip-an-effect-on-updates)
@@ -403,7 +404,7 @@ function Example() {
403
404
404
405
Если вы намеренно хотите считать из асинхронного колбэка *свежайшее* состояние, вы можете сперва сохранить его [в реф](/docs/hooks-faq.html#is-there-something-like-instance-variables), потом изменить его и затем считать его из рефа.
405
406
406
-
Наконец, возможна другая ситуация, почему вы видите устаревшие пропсы или состояние: когда вы используете оптимизацию с помощью «массива зависимостей», но неправильно указали какие-то зависимости. Например, если эффект передаёт вторым параметром `[]`, но при этом использует `someProp`, то он продолжит «видеть» исходное значение `someProp`. Правильным решением является либо исправление массива, либо отказ от его использования.
407
+
Наконец, возможна другая ситуация, почему вы видите устаревшие пропсы или состояние: когда вы используете оптимизацию с помощью «массива зависимостей», но неправильно указали какие-то зависимости. Например, если эффект передаёт вторым параметром `[]`, но при этом использует `someProp`, то он продолжит «видеть» исходное значение `someProp`. Правильным решением является либо исправление массива, либо отказ от его использования.
407
408
По этим ссылкам описаны [подходы к функциям](#is-it-safe-to-omit-functions-from-the-list-of-dependencies) в аналогичных ситуациях и [другие известные способы](#what-can-i-do-if-my-effect-dependencies-change-too-often) снижения частоты вызова эффектов, исключающие передачу неправильных зависимостей.
408
409
409
410
>Примечание
@@ -451,9 +452,62 @@ function ScrollView({row}) {
451
452
452
453
### Могу ли я изменить реф, переданный в функциональный компонент? {#can-i-make-a-ref-to-a-function-component}
453
454
454
-
455
455
Несмотря на то, что вам не понадобится это часто, вы можете предоставить некоторые императивные методы родительскому компоненту, используя хук [`useImperativeHandle`](/docs/hooks-reference.html#useimperativehandle).
456
456
457
+
### Как я могу измерить узел DOM? {#how-can-i-measure-a-dom-node}
458
+
459
+
Для определения положения или размера DOM-узла можно использовать [колбэк-реф](/docs/refs-and-the-dom.html#callback-refs). React будет вызывать этот колбэк всякий раз, когда реф привязывается к другому узлу. Вот [небольшая демонстрация](https://codesandbox.io/s/l7m0v5x4v9):
460
+
461
+
```js{4-8,12}
462
+
function MeasureExample() {
463
+
const [height, setHeight] = useState(0);
464
+
465
+
const measuredRef = useCallback(node => {
466
+
if (node !== null) {
467
+
setHeight(node.getBoundingClientRect().height);
468
+
}
469
+
}, []);
470
+
471
+
return (
472
+
<>
473
+
<h1 ref={measuredRef}>Привет, мир</h1>
474
+
<h2>Заголовок выше имеет высоту {Math.round(height)} пикселей</h2>
475
+
</>
476
+
);
477
+
}
478
+
```
479
+
480
+
Мы не выбрали `useRef` в этом примере, потому что объект рефа не уведомляет нас об *изменениях* текущего значения рефа. Использование колбэк-рефа гарантирует, что [даже если дочерний компонент отображает измеренный узел позже](https://codesandbox.io/s/818zzk8m78) (например, в ответ на клик), мы по-прежнему получаем уведомление об этом в родительском компоненте и можем обновлять измерения.
481
+
482
+
Обратите внимание, что мы передаем `[]` как массив зависимостей в `useCallback`. Это гарантирует, что наш колбэк-реф не изменится между повторными рендерами, а значит React не будет вызывать его без необходимости.
483
+
484
+
При желании вы можете [извлечь эту логику](https://codesandbox.io/s/m5o42082xy) в переиспользуемый хук:
485
+
486
+
```js{2}
487
+
function MeasureExample() {
488
+
const [rect, ref] = useClientRect();
489
+
return (
490
+
<>
491
+
<h1 ref={ref}>Привет, мир</h1>
492
+
{rect !== null &&
493
+
<h2>Заголовок выше имеет высоту {Math.round(height)} пикселей</h2>
494
+
}
495
+
</>
496
+
);
497
+
}
498
+
499
+
function useClientRect() {
500
+
const [rect, setRect] = useState(null);
501
+
const ref = useCallback(node => {
502
+
if (node !== null) {
503
+
setRect(node.getBoundingClientRect());
504
+
}
505
+
}, []);
506
+
return [rect, ref];
507
+
}
508
+
```
509
+
510
+
457
511
### Что значит `const [thing, setThing] = useState()`? {#what-does-const-thing-setthing--usestate-mean}
458
512
459
513
Если вы не знакомы с этим синтаксисом, ознакомьтесь с [объяснением](/docs/hooks-state.html#tip-what-do-square-brackets-mean) в документации хука состояния.
@@ -857,8 +911,8 @@ function Form() {
857
911
const [text, updateText] = useState('');
858
912
const textRef = useRef();
859
913
860
-
useLayoutEffect(() => {
861
-
textRef.current = text; // Записать в реф
914
+
useEffect(() => {
915
+
textRef.current = text; // Записать это в реф
862
916
});
863
917
864
918
const handleSubmit = useCallback(() => {
@@ -898,7 +952,7 @@ function useEventCallback(fn, dependencies) {
898
952
throw new Error('Невозможно вызвать обработчик события во время рендера.');
Если вы обновите состояние хука тем же значением, что и текущее состояние, React досрочно выйдет из хука без повторного рендера дочерних элементов и запуска эффектов. (React использует [алгоритм сравнения `Object.is`](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).)
98
98
99
+
Обратите внимание, что для React всё еще может быть необходим повторный рендер этого компонента. Это не должно быть проблемой, потому что React не будет сильно «углубляться» в дерево. Если вы делаете дорогостоящие вычисления во время рендеринга, вы можете оптимизировать их с помощью `useMemo`.
100
+
99
101
### `useEffect` {#useeffect}
100
102
101
103
```js
@@ -171,12 +173,28 @@ useEffect(
171
173
### `useContext` {#usecontext}
172
174
173
175
```js
174
-
constcontext=useContext(Context);
176
+
constvalue=useContext(MyContext);
175
177
```
176
178
177
-
Принимает объект контекста (значение, возвращённое из `React.createContext`) и возвращает текущее значение контекста, как указано ближайшим поставщиком контекста для данного контекста.
179
+
Принимает объект контекста (значение, возвращаемое из `React.createContext`) и возвращает текущее значение контекста для этого контекста. Текущее значение контекста определяется пропом `value` ближайшего `<MyContext.Provider>` над вызывающим компонентом в дереве.
180
+
181
+
Когда ближайший `<MyContext.Provider>` над компонентом обновляется, этот хук вызовет повторный рендер с последним значением контекста, переданным этому провайдеру `MyContext`.
182
+
183
+
Запомните, аргумент для `useContext` должен быть *непосредственно сам объект контекста*:
184
+
185
+
***Правильно:**`useContext(MyContext)`
186
+
***Неправильно:**`useContext(MyContext.Consumer)`
187
+
***Неправильно:**`useContext(MyContext.Provider)`
188
+
178
189
179
-
Когда провайдер обновляется, этот хук инициирует повторный рендер с последним значением контекста.
190
+
Компонент, вызывающий `useContext`, всегда будет перерендериваться при изменении значения контекста. Если повторный рендер компонента затратен, вы можете [оптимизировать его с помощью мемоизации](https://github.com/facebook/react/issues/15156#issuecomment-474590693).
191
+
192
+
>Совет
193
+
>
194
+
195
+
>Если вы были знакомы с API контекстов до появления хуков, то вызов `useContext(MyContext)` аналогичен выражению `static contextType = MyContext` в классе, либо компоненту `<MyContext.Provider>`.
196
+
>
197
+
>`useContext (MyContext)` позволяет только *читать* контекст и подписываться на его изменения. Вам всё ещё нужен `<MyContext.Provider>` выше в дереве, чтобы *предоставить* значение для этого контекста.
180
198
181
199
## Дополнительные хуки {#additional-hooks}
182
200
@@ -283,6 +301,8 @@ function Counter({initialCount}) {
283
301
284
302
Если вы вернёте то же значение из редюсера хука, что и текущее состояние, React выйдет без перерисовки дочерних элементов или запуска эффектов. (React использует [алгоритм сравнения Object.is](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/is#Description).)
285
303
304
+
Обратите внимание, что для React всё еще может быть необходим повторный рендер этого компонента. Это не должно быть проблемой, потому что React не будет сильно «углубляться» в дерево. Если вы делаете дорогостоящие вычисления во время рендеринга, вы можете оптимизировать их с помощью `useMemo`.
305
+
286
306
### `useCallback` {#usecallback}
287
307
288
308
```js
@@ -354,7 +374,16 @@ function TextInputWithFocusButton() {
354
374
}
355
375
```
356
376
357
-
Обратите внимание, что `useRef()` полезен не только для атрибута `ref`. Он также [удобен для хранения любого изменяемого значения](/docs/hooks-faq.html#is-there-something-like-instance-variables) примерно так же, как вы используете поля экземпляров в классах.
377
+
По сути, `useRef` похож на «коробку», которая может содержать изменяемое значение в своём свойстве `.current`.
378
+
379
+
Возможно, вы знакомы с рефами в основном как со способом [получить доступ к DOM](/docs/refs-and-the-dom.html). Если вы передадите React объект рефа с помощью подобного выражения `<div ref={myRef}/>`, React установит собственное свойство `.current` на соответствующий DOM-узел при каждом его изменении.
380
+
381
+
Но хук `useRef()` полезен не только установкой атрибута с рефом. Он [удобен для сохранения любого мутируемого значения](/docs/hooks-faq.html#is-there-something-like-instance-variables), по аналогии с тем, как вы используете поля экземпляра в классах.
382
+
383
+
Это возможно, поскольку `useRef()` создаёт обычный JavaScript-объект. Единственная разница между `useRef()` и просто созданием самого объекта `{current: ...}` — это то, что хук `useRef` даст один и тот же объект с рефом при каждом рендере.
384
+
385
+
Имейте в виду, что `useRef`*не* уведомляет вас, когда изменяется его содержимое. Мутирование свойства `.current` не вызывает повторный рендер. Если вы хотите запустить некоторый код, когда React присоединяет или отсоединяет реф к узлу DOM, вы можете использовать [колбэк-реф](/docs/hooks-faq.html#how-can-i-measure-a-dom-node) вместо этого.
> Если вы переносите код из классового компонента, `useLayoutEffect` запускается в той же фазе, что и `componentDidMount` и `componentDidUpdate`, поэтому, если вы не уверены, какой хук эффект использовать, это, вероятно, наименее рискованно.
419
+
> Если вы переносите код из классового компонента, `useLayoutEffect` запускается в той же фазе, что и `componentDidMount` и `componentDidUpdate`. Тем не менее, *мы рекомендуем начать с `useEffect`**, и попробовать использовать `useLayoutEffect`, если тот приводит к возникновению проблем.
420
+
>
421
+
>Если вы используете серверный рендеринг, имейте в виду, что *ни*`useLayoutEffect`, ни `useEffect` не могут работать до загрузки JavaScript. Вот почему React предупреждает, когда серверный компонент содержит `useLayoutEffect`. Чтобы справиться с данной проблемой, либо переместите эту логику в `useEffect` (если она не нужна для первого рендера), либо задержите отображение этого компонента до тех пор, пока не выполнится рендеринг на стороне клиента (если HTML некорректный до запуска `useLayoutEffect`).
422
+
>
423
+
>Чтобы исключить компонент, который нуждается в эффектах макета из HTML-кода, полученного в результате серверного рендеринга, выполните его рендер по условию `showChild && <Child />` и отложите отображение с помощью `useEffect(() => { setShowChild(true); }, [])``. Таким образом, пользовательский интерфейс не будет выглядеть некорректно перед гидратацией.
0 commit comments