что такое cleanup для useeffect

Why is the cleanup function from `useEffect` called on every render?

I’ve been learning React and I read that the function returned from useEffect is meant to do cleanup and React performs the cleanup when the component unmounts.

So I experimented with it a bit but found in the following example that the function was called every time the component re-renders as opposed to only the time it got unmounted from the DOM, i.e. it console.log(«unmount»); every time the component re-renders.

что такое cleanup для useeffect. Смотреть фото что такое cleanup для useeffect. Смотреть картинку что такое cleanup для useeffect. Картинка про что такое cleanup для useeffect. Фото что такое cleanup для useeffect

что такое cleanup для useeffect. Смотреть фото что такое cleanup для useeffect. Смотреть картинку что такое cleanup для useeffect. Картинка про что такое cleanup для useeffect. Фото что такое cleanup для useeffect

5 Answers 5

React performs the cleanup when the component unmounts.

I’m not sure where you read this but this statement is incorrect. React performs the cleanup when the dependencies to that hook changes and the effect hook needs to run again with new values. This behaviour is intentional to maintain the reactivity of the view to changing data. Going off the official example, let’s say an app subscribes to status updates from a friends’ profile. Being the great friend you are, you are decide to unfriend them and befriend someone else. Now the app needs to unsubscribe from the previous friend’s status updates and listen to updates from your new friend. This is natural and easy to achieve with the way useEffect works.

By including the friend id in the dependency list, we can indicate that the hook needs to run only when the friend id changes.

In your example you have specified the array in the dependency list and you are changing the array at a set interval. Every time you change the array, the hook reruns.

You can achieve the correct functionality simply by removing the array from the dependency list and using the callback version of the setState hook. The callback version always operates on the previous version of the state, so there is no need to refresh the hook every time the array changes.

Some additional feedback would be to use the id directly in clearInterval as the value is closed upon (captured) when you create the cleanup function. There is no need to save it to a ref.

Источник

Using the Effect Hook

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

The Effect Hook lets you perform side effects in function components:

This snippet is based on the counter example from the previous page, but we added a new feature to it: we set the document title to a custom message including the number of clicks.

Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you’re used to calling these operations “side effects” (or just “effects”), you’ve likely performed them in your components before.

There are two common kinds of side effects in React components: those that don’t require cleanup, and those that do. Let’s look at this distinction in more detail.

Effects Without Cleanup

Sometimes, we want to run some additional code after React has updated the DOM. Network requests, manual DOM mutations, and logging are common examples of effects that don’t require a cleanup. We say that because we can run them and immediately forget about them. Let’s compare how classes and Hooks let us express such side effects.

Example Using Classes

In React class components, the render method itself shouldn’t cause side effects. It would be too early — we typically want to perform our effects after React has updated the DOM.

Note how we have to duplicate the code between these two lifecycle methods in class.

This is because in many cases we want to perform the same side effect regardless of whether the component just mounted, or if it has been updated. Conceptually, we want it to happen after every render — but React class components don’t have a method like this. We could extract a separate method but we would still have to call it in two places.

Now let’s see how we can do the same with the useEffect Hook.

Example Using Hooks

We’ve already seen this example at the top of this page, but let’s take a closer look at it:

What does useEffect do? By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.

Why is useEffect called inside a component? Placing useEffect inside the component lets us access the count state variable (or any props) right from the effect. We don’t need a special API to read it — it’s already in the function scope. Hooks embrace JavaScript closures and avoid introducing React-specific APIs where JavaScript already provides a solution.

Does useEffect run after every render? Yes! By default, it runs both after the first render and after every update. (We will later talk about how to customize this.) Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.

Now that we know more about effects, these lines should make sense:

We declare the count state variable, and then we tell React we need to use an effect. We pass a function to the useEffect Hook. This function we pass is our effect. Inside our effect, we set the document title using the document.title browser API. We can read the latest count inside the effect because it’s in the scope of our function. When React renders our component, it will remember the effect we used, and then run our effect after updating the DOM. This happens for every render, including the first one.

Experienced JavaScript developers might notice that the function passed to useEffect is going to be different on every render. This is intentional. In fact, this is what lets us read the count value from inside the effect without worrying about it getting stale. Every time we re-render, we schedule a different effect, replacing the previous one. In a way, this makes the effects behave more like a part of the render result — each effect “belongs” to a particular render. We will see more clearly why this is useful later on this page.

Effects with Cleanup

Earlier, we looked at how to express side effects that don’t require any cleanup. However, some effects do. For example, we might want to set up a subscription to some external data source. In that case, it is important to clean up so that we don’t introduce a memory leak! Let’s compare how we can do it with classes and with Hooks.

Example Using Classes

Notice how componentDidMount and componentWillUnmount need to mirror each other. Lifecycle methods force us to split this logic even though conceptually code in both of them is related to the same effect.

Eagle-eyed readers may notice that this example also needs a componentDidUpdate method to be fully correct. We’ll ignore this for now but will come back to it in a later section of this page.

Example Using Hooks

Let’s see how we could write this component with Hooks.

You might be thinking that we’d need a separate effect to perform the cleanup. But code for adding and removing a subscription is so tightly related that useEffect is designed to keep it together. If your effect returns a function, React will run it when it is time to clean up:

Why did we return a function from our effect? This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They’re part of the same effect!

When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time. We’ll discuss why this helps avoid bugs and how to opt out of this behavior in case it creates performance issues later below.

We don’t have to return a named function from the effect. We called it cleanup here to clarify its purpose, but you could return an arrow function or call it something different.

We’ve learned that useEffect lets us express different kinds of side effects after a component renders. Some effects might require cleanup so they return a function:

Other effects might not have a cleanup phase, and don’t return anything.

The Effect Hook unifies both use cases with a single API.

If you feel like you have a decent grasp on how the Effect Hook works, or if you feel overwhelmed, you can jump to the next page about Rules of Hooks now.

Tips for Using Effects

We’ll continue this page with an in-depth look at some aspects of useEffect that experienced React users will likely be curious about. Don’t feel obligated to dig into them now. You can always come back to this page to learn more details about the Effect Hook.

Tip: Use Multiple Effects to Separate Concerns

One of the problems we outlined in the Motivation for Hooks is that class lifecycle methods often contain unrelated logic, but related logic gets broken up into several methods. Here is a component that combines the counter and the friend status indicator logic from the previous examples:

So, how can Hooks solve this problem? Just like you can use the State Hook more than once, you can also use several effects. This lets us separate unrelated logic into different effects:

Hooks let us split the code based on what it is doing rather than a lifecycle method name. React will apply every effect used by the component, in the order they were specified.

Explanation: Why Effects Run on Each Update

If you’re used to classes, you might be wondering why the effect cleanup phase happens after every re-render, and not just once during unmounting. Let’s look at a practical example to see why this design helps us create components with fewer bugs.

But what happens if the friend prop changes while the component is on the screen? Our component would continue displaying the online status of a different friend. This is a bug. We would also cause a memory leak or crash when unmounting since the unsubscribe call would use the wrong friend ID.

In a class component, we would need to add componentDidUpdate to handle this case:

Forgetting to handle componentDidUpdate properly is a common source of bugs in React applications.

Now consider the version of this component that uses Hooks:

It doesn’t suffer from this bug. (But we also didn’t make any changes to it.)

There is no special code for handling updates because useEffect handles them by default. It cleans up the previous effects before applying the next effects. To illustrate this, here is a sequence of subscribe and unsubscribe calls that this component could produce over time:

This behavior ensures consistency by default and prevents bugs that are common in class components due to missing update logic.

Tip: Optimizing Performance by Skipping Effects

In some cases, cleaning up or applying the effect after every render might create a performance problem. In class components, we can solve this by writing an extra comparison with prevProps or prevState inside componentDidUpdate :

This requirement is common enough that it is built into the useEffect Hook API. You can tell React to skip applying an effect if certain values haven’t changed between re-renders. To do so, pass an array as an optional second argument to useEffect :

This also works for effects that have a cleanup phase:

In the future, the second argument might get added automatically by a build-time transformation.

If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders. Learn more about how to deal with functions and what to do when the array changes too often.

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ( [] ) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run. This isn’t handled as a special case — it follows directly from how the dependencies array always works.

If you pass an empty array ( [] ), the props and state inside the effect will always have their initial values. While passing [] as the second argument is closer to the familiar componentDidMount and componentWillUnmount mental model, there are usually better solutions to avoid re-running effects too often. Also, don’t forget that React defers running useEffect until after the browser has painted, so doing extra work is less of a problem.

We recommend using the exhaustive-deps rule as part of our eslint-plugin-react-hooks package. It warns when dependencies are specified incorrectly and suggests a fix.

Congratulations! This was a long page, but hopefully by the end most of your questions about effects were answered. You’ve learned both the State Hook and the Effect Hook, and there is a lot you can do with both of them combined. They cover most of the use cases for classes — and where they don’t, you might find the additional Hooks helpful.

At this point you might be questioning how Hooks work. How can React know which useState call corresponds to which state variable between re-renders? How does React “match up” previous and next effects on every update? On the next page we will learn about the Rules of Hooks — they’re essential to making Hooks work.

Источник

Справочник API хуков

Хуки — нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.

На этой странице описан API, относящийся к встроенным хукам React.

Если вы новичок в хуках, вы можете сначала ознакомиться с общим обзором. Вы также можете найти полезную информацию в главе «Хуки: ответы на вопросы».

Возвращает значение с состоянием и функцию для его обновления.

Во время первоначального рендеринга возвращаемое состояние ( state ) совпадает со значением, переданным в качестве первого аргумента ( initialState ).

Функция setState используется для обновления состояния. Она принимает новое значение состояния и ставит в очередь повторный рендер компонента.

Кнопки «+» и «-» используют функциональную форму, потому что обновлённое значение основано на предыдущем значении. Но кнопка «Сбросить» использует обычную форму, потому что она всегда устанавливает счётчик обратно в 0.

Если функция обновления возвращает абсолютно такой же результат как и текущее состояние, то последующие повторные рендеры будут полностью пропущены.

Ленивая инициализация состояния

Аргумент initialState — это состояние, используемое во время начального рендеринга. В последующих рендерах это не учитывается. Если начальное состояние является результатом дорогостоящих вычислений, вы можете вместо этого предоставить функцию, которая будет выполняться только при начальном рендеринге:

Досрочное прекращение обновления состояния

Принимает функцию, которая содержит императивный код, возможно, с эффектами.

Мутации, подписки, таймеры, логирование и другие побочные эффекты не допускаются внутри основного тела функционального компонента (называемого этапом рендеринга React). Это приведёт к запутанным ошибкам и несоответствиям в пользовательском интерфейсе.

По умолчанию эффекты запускаются после каждого завершённого рендеринга, но вы можете решить запускать их только при изменении определённых значений.

Функция очистки запускается до удаления компонента из пользовательского интерфейса, чтобы предотвратить утечки памяти. Кроме того, если компонент рендерится несколько раз (как обычно происходит), предыдущий эффект очищается перед выполнением следующего эффекта. В нашем примере это означает, что новая подписка создаётся при каждом обновлении. Чтобы избежать воздействия на каждое обновление, обратитесь к следующему разделу.

Порядок срабатывания эффектов

Хотя useEffect откладывается до тех пор, пока браузер не выполнит отрисовку, он гарантированно срабатывает перед любыми новыми рендерами. React всегда полностью применяет эффекты предыдущего рендера перед началом нового обновления.

Условное срабатывание эффекта

По умолчанию эффекты запускаются после каждого завершённого рендера. Таким образом, эффект всегда пересоздаётся, если значение какой-то из зависимости изменилось.

Если вы хотите использовать эту оптимизацию, обратите внимание на то, чтобы массив включал в себя все значения из области видимости компонента (такие как пропсы и состояние), которые могут изменяться с течением времени, и которые будут использоваться эффектом. В противном случае, ваш код будет ссылаться на устаревшее значение из предыдущих рендеров. Отдельные страницы документации рассказывают о том, как поступить с функциями и что делать с часто изменяющимися массивами.

Если вы хотите запустить эффект и сбросить его только один раз (при монтировании и размонтировании), вы можете передать пустой массив ( [] ) вторым аргументом. React посчитает, что ваш эффект не зависит от каких-либо значений из пропсов или состояния и поэтому не будет выполнять повторных запусков эффекта. Это не обрабатывается как особый случай — он напрямую следует из логики работы входных массивов.

Массив зависимостей не передаётся в качестве аргументов функции эффекта. Тем не менее, в теории вот что происходит: каждое значение, на которое ссылается функция эффекта, должно также появиться в массиве зависимостей. В будущем достаточно продвинутый компилятор сможет создать этот массив автоматически.

Принимает объект контекста (значение, возвращённое из React.createContext ) и возвращает текущее значение контекста для этого контекста. Текущее значение контекста определяется пропом value ближайшего над вызывающим компонентом в дереве.

Запомните, аргументом для useContext должен быть непосредственно сам объект контекста:

useContext(MyContext) позволяет только читать контекст и подписываться на его изменения. Вам всё ещё нужен выше в дереве, чтобы предоставить значение для этого контекста.

Соединим все вместе с Context.Provider

Это пример из раздела Продвинутые темы: Контекст, только переписанный с использованием хуков. В этом же разделе можно найти больше информации о том, как и когда использовать объект Context.

Следующие хуки являются вариантами базовых из предыдущего раздела или необходимы только для конкретных крайних случаев. Их не требуется основательно изучать заранее.

Указание начального состояния

Это позволяет извлечь логику для расчёта начального состояния за пределы редюсера. Это также удобно для сброса состояния позже в ответ на действие:

Досрочное прекращение dispatch

Если вы вернёте то же значение из редюсера хука, что и текущее состояние, React выйдет без перерисовки дочерних элементов или запуска эффектов. (React использует алгоритм сравнения Object.is.)

Передайте встроенный колбэк и массив зависимостей. Хук useCallback вернёт мемоизированную версию колбэка, который изменяется только, если изменяются значения одной из зависимостей. Это полезно при передаче колбэков оптимизированным дочерним компонентам, которые полагаются на равенство ссылок для предотвращения ненужных рендеров (например, shouldComponentUpdate ).

Массив зависимостей не передаётся в качестве аргументов для колбэка. Концептуально, однако, это то, что они представляют: каждое значение, использованное в колбэке, должно также появиться в массиве зависимостей. В будущем достаточно продвинутый компилятор может создать этот массив автоматически.

Передайте «создающую» функцию и массив зависимостей. useMemo будет повторно вычислять мемоизированное значение только тогда, когда значение какой-либо из зависимостей изменилось. Эта оптимизация помогает избежать дорогостоящих вычислений при каждом рендере.

Если массив не был передан, новое значение будет вычисляться при каждом рендере.

Массив зависимостей не передаётся в качестве аргументов функции. Концептуально, однако, это то, что они представляют: каждое значение, на которое ссылается функция, должно также появиться в массиве зависимостей. В будущем достаточно продвинутый компилятор может создать этот массив автоматически.

Обычный случай использования — это доступ к потомку в императивном стиле:

Возможно, вы знакомы с рефами в основном как со способом получить доступ к DOM. Если вы передадите React объект рефа с помощью подобного выражения

Но хук useRef() полезен не только установкой атрибута с рефом. Он удобен для сохранения любого мутируемого значения, по аналогии с тем, как вы используете поля экземпляра в классах.

useDebugValue может использоваться для отображения метки для пользовательских хуков в React DevTools.

Мы не рекомендуем добавлять значения отладки в каждый пользовательский хук. Это наиболее ценно для пользовательских хуков, которые являются частью общих библиотек.

Отложите форматирование значений отладки

В некоторых случаях форматирование значения для отображения может быть дорогой операцией. Это также не нужно, если хук не проверен.

По этой причине useDebugValue принимает функцию форматирования в качестве необязательного второго параметра. Эта функция вызывается только при проверке хуков. Она получает значение отладки в качестве параметра и должна возвращать форматированное отображаемое значение.

Источник

3.12.4 Хук эффекта

Хуки доступны в версии React 16.8. Они позволяют использовать состояние и другие функции React, освобождая от необходимости писать класс.

Побочные эффекты можно выполнять в компонентах-функциях используя хука эффекта:

Этот фрагмент кода основан на примере счетчика из предыдущего раздела. Однако мы добавили в него новую функцию: мы устанавливаем название документа, содержащее колличество нажатий.

Компоненты React имеют два основных вида побочных эффектов: требующие очистки, и не требующие. Давайте разберём это различие более подробно.

3.12.4.1 Эффекты, не требующие очистки

3.12.4.1.1 Пример с использованием класса

Заметьте, как в классе нам приходится дублировать код в этих двух методах ЖЦ.

Так происходит потому, что нам обычно нужно выполнить один и тот же побочный эффект независимо от того, был ли компонент монтирован или обновлен. Концептуально, мы хотим, чтобы это происходило после каждой отрисовки, но у классов нет такого метода. Мы могли бы вынести код в отдельный метод, но нам все равно пришлось бы вызывать его в двух местах.

3.12.4.1.2 Пример с использованием хука

Мы уже видели этот пример выше. Давайте рассмотрим его более подробно:

Что делает useEffect? Используя этот хук, вы сообщаете React, что ваш компонент должен что-то делать после отрисовки. React запомнит переданную вами функцию (мы будем называть ее «эффектом») и вызовет ее после обновления DOM. В нашем случае мы устанавливаем название документа. Кроме этого мы можем извлекать данные или вызывать любой другой императивный API.

Запускается ли useEffect после каждой отрисовки? Да! По умолчанию он запускается как после первой отрисовки, так и после каждого последующего обновления. (Позже мы поговорим о том, как это можно кастомизировать.) Вместо того, чтобы мыслить в терминах «монтирования» и «обновления», можно просто представлять, что эффекты происходят «после отрисовки». React гарантирует, что DOM будет обновлен к моменту запуска эффектов.

3.12.4.1.3 Детальный разбор

Узнав больше об эффектах, этот код становится понятней:

3.12.4.2 Эффекты с очисткой

Ранее мы рассмотрели, как создавать побочные эффекты, которые не требуют какой-либо очистки. Однако некоторым эффектам она всё же нужна. Допустим, нам нужно настроить подписку на некоторый внешний источник данных. В этом случае важно провести очистку, чтобы избежать утечек памяти! Давайте сравним, как мы можем выполнить очистку в классах и с использованием хуков.

3.12.4.2.1 Пример с использованием класса

Обратите внимание, что componentDidMount и componentWillUnmount должны быть зеркальны друг другу. Методы ЖЦ заставляют нас размещать коды подписки и отписки по разным местам, хотя концептуально код в обоих частях этой логики связан с одним и тем же эффектом.

3.12.4.2.2 Пример с использованием хука

А теперь посмотрим, как написать этот компонент, используя функционал хуков.

Возможно, вы подумали, что для очистки нам понадобится отдельный эффект. Но коды для добавления и удаления подписки связаны настолько тесно, что хук useEffect специально разработан с учетом того, чтобы поместить их вместе. Если ваш эффект возвращает функцию, React выполнит её, когда придет время для очистки:

Почему мы вернули функцию из нашего эффекта? Это опциональный механизм очистки для эффектов. Каждый эффект может возвращать функцию, которая после него выполнит очистку. Это позволяет нам поддерживать коды добавления и удаления подписок максимально близко друг к другу. Они являются частью одного эффекта!

Когда именно React выполняет очистку в эффекте? React производит очистку, когда компонент демонтируется. Однако, как мы уже знаем, эффекты запускаются для каждой отрисовки, а не единожды. Вот почему React также очищает эффекты предыдущей отрисовки, прежде чем запускать эффекты снова. Далее мы обсудим, почему это помогает избежать ошибок и как отказаться от такого поведения в том случае, если это создает проблемы с производительностью.

Вы не обязаны возвращать именованную функцию из эффекта. Функцию выше мы назвали cleanup только для того, чтобы прояснить её предназначение. Вы также можете вернуть стрелочную функцию либо вызвать какой-то другой код.

3.12.4.3 Резюме

Мы узнали, что useEffect позволяет определять различные виды побочных эффектов, происходящих после отрисовки компонента. Некоторые эффекты могут требовать очистку, поэтому они должны возвращать функцию:

Эффекты, не имеющие фазы очистки, ничего не возвращают.

Хук эффекта объединяет оба случая под одним API.

Если вы чувствуете, что у вас появилось неплохое понимание того, как работает хук эффекта, вы можете перейти к следующему разделу о правилах использования хуков прямо сейчас.

3.12.4.4 Подсказки по использованию эффектов

3.12.4.4.1 Совет: используйте несколько эффектов для разделения задач

Одна из проблем, которую мы описали в пункте о мотивации, заключается в том, что методы ЖЦ класса часто содержат несвязанную логику, а связанная логика, наоборот, разбита по разным методам ЖЦ. Вот компонент, который совмещает счетчик и логику индикатора состояния друга из предыдущих примеров:

Как же хуки помогают решить эту проблему? По аналогии с многократным использованием хука состояния, можно использовать несколько эффектов. Это позволяет нам разбить несвязанную логику на разные эффекты:

Хуки позволяют разделить код на основе того, что он делает, а не на основании имён методов ЖЦ. React будет применять каждый эффект, используемый компонентом, в указанном порядке.

3.12.4.4.2 Объяснение: почему эффекты выполняются для каждого обновления?

Если вы привыкли к классам, вам может быть интересно, почему фаза очистки эффекта происходит после каждой повторной отрисовки, а не однократно при демонтировании. Давайте рассмотрим практический пример, чтобы понять, почему именно такой дизайн помогает создавать компоненты с меньшим количеством ошибок.

Но что произойдет, если свойство friend изменится, пока компонент отображается на экране? Наш компонент будет продолжать отображать онлайн-статус, но. другого друга. Это ошибка. Также мы могли бы вызвать утечку памяти или крэш при демонтировании, так как вызов отмены подписки будет использовать неверный ID друга.

В компоненте-классе для обработки такого случая нам нужно добавить componentDidUpdate :

Отсутствие правильной обработки в componentDidUpdate является распространенным источником ошибок в приложениях React.

Теперь рассмотрим версию этого компонента, которая использует хуки:

Она не страдает от такой ошибки. (Помимо прочего, мы не внесли никаких изменений.)

Не существует специального кода для обработки обновлений, потому что useEffect обрабатывает их по умолчанию. Он очищает предыдущие эффекты перед применением следующих эффектов. Для наглядной иллюстрации вот вам последовательность вызовов подписки и отмены подписки, которые этот компонент может производить с течением времени:

Такое поведение обеспечивает согласованность по умолчанию и предотвращает ошибки, которые часто встречаются в компонентах-классах из-за отсутствия логики обновления.

3.12.4.4.3 Подсказка: как повысить производительность, указывая React пропустить срабатывание эффекта

В некоторых случаях очистка или применение эффекта после каждой отрисовки может привести к проблемам с производительностью. В компонентах-классах мы можем решить эту проблему, написав дополнительное сравнение с prevProps или prevState внутри componentDidUpdate :

Это также справедливо для эффектов, которые имеют фазу очистки:

В будущем второй аргумент может быть добавлен автоматически при преобразовании во время сборки.

Если вы используете эту оптимизацию, убедитесь, что массив содержит все значения из окружающей области видимости, которые изменяются со временем и используются эффектом. В противном случае ваш код будет ссылаться на устаревшие значения из предыдущих отрисовок. Также мы обсудим и другие варианты оптимизации в справке по API хуков.

3.12.4.5 Следующие шаги

Поздравляем! Это был довольно большой раздел, и мы надеемся, что к его концу нам удалось ответить на большинство ваших вопросов об эффектах. Вы изучили хук состояния и хук эффекта. Вместе они позволяют решить широкий спектр задач, охватывая большую часть случаев, где ранее использовались классы. В оставшихся случаях, могут оказаться полезными дополнительные хуки.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *