usecallback hook react зачем
Hooks API Reference
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
This page describes the APIs for the built-in Hooks in React.
If you’re new to Hooks, you might want to check out the overview first. You may also find useful information in the frequently asked questions section.
Returns a stateful value, and a function to update it.
During the initial render, the returned state ( state ) is the same as the value passed as the first argument ( initialState ).
The setState function is used to update the state. It accepts a new state value and enqueues a re-render of the component.
During subsequent re-renders, the first value returned by useState will always be the most recent state after applying updates.
React guarantees that setState function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
The ”+” and ”-” buttons use the functional form, because the updated value is based on the previous value. But the “Reset” button uses the normal form, because it always sets the count back to the initial value.
If your update function returns the exact same value as the current state, the subsequent rerender will be skipped completely.
Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
Lazy initial state
The initialState argument is the state used during the initial render. In subsequent renders, it is disregarded. If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render:
Bailing out of a state update
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)
Accepts a function that contains imperative, possibly effectful code.
Mutations, subscriptions, timers, logging, and other side effects are not allowed inside the main body of a function component (referred to as React’s render phase). Doing so will lead to confusing bugs and inconsistencies in the UI.
By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.
Cleaning up an effect
Often, effects create resources that need to be cleaned up before the component leaves the screen, such as a subscription or timer ID. To do this, the function passed to useEffect may return a clean-up function. For example, to create a subscription:
The clean-up function runs before the component is removed from the UI to prevent memory leaks. Additionally, if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect. In our example, this means a new subscription is created on every update. To avoid firing an effect on every update, refer to the next section.
Although useEffect is deferred until after the browser has painted, it’s guaranteed to fire before any new renders. React will always flush a previous render’s effects before starting a new update.
Conditionally firing an effect
The default behavior for effects is to fire the effect after every completed render. That way an effect is always recreated if one of its dependencies changes.
However, this may be overkill in some cases, like the subscription example from the previous section. We don’t need to create a new subscription on every update, only if the source prop has changed.
To implement this, pass a second argument to useEffect that is the array of values that the effect depends on. Our updated example now looks like this:
Now the subscription will only be recreated when props.source changes.
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 values change 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.
The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that’s what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
Accepts a context object (the value returned from React.createContext ) and returns the current context value for that context. The current context value is determined by the value prop of the nearest above the calling component in the tree.
Don’t forget that the argument to useContext must be the context object itself:
A component calling useContext will always re-render when the context value changes. If re-rendering the component is expensive, you can optimize it by using memoization.
useContext(MyContext) only lets you read the context and subscribe to its changes. You still need a above in the tree to provide the value for this context.
Putting it together with Context.Provider
This example is modified for hooks from a previous example in the Context Advanced Guide, where you can find more information about when and how to use Context.
The following Hooks are either variants of the basic ones from the previous section, or only needed for specific edge cases. Don’t stress about learning them up front.
useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.
Here’s the counter example from the useState section, rewritten to use a reducer:
React guarantees that dispatch function identity is stable and won’t change on re-renders. This is why it’s safe to omit from the useEffect or useCallback dependency list.
Specifying the initial state
There are two different ways to initialize useReducer state. You may choose either one depending on the use case. The simplest way is to pass the initial state as a second argument:
React doesn’t use the state = initialState argument convention popularized by Redux. The initial value sometimes needs to depend on props and so is specified from the Hook call instead. If you feel strongly about this, you can call useReducer(reducer, undefined, reducer) to emulate the Redux behavior, but it’s not encouraged.
It lets you extract the logic for calculating the initial state outside the reducer. This is also handy for resetting the state later in response to an action:
Bailing out of a dispatch
If you return the same value from a Reducer Hook as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)
Returns a memoized callback.
Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate ).
The array of dependencies is not passed as arguments to the callback. Conceptually, though, that’s what they represent: every value referenced inside the callback should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
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.
Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
If no array is provided, a new value will be computed on every render.
You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.
The array of dependencies is not passed as arguments to the function. Conceptually, though, that’s what they represent: every value referenced inside the function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.
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.
A common use case is to access a child imperatively:
You might be familiar with refs primarily as a way to access the DOM. If you pass a ref object to React with
However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.
Prefer the standard useEffect when possible to avoid blocking visual updates.
useDebugValue can be used to display a label for custom hooks in React DevTools.
For example, consider the useFriendStatus custom Hook described in “Building Your Own Hooks”:
We don’t recommend adding debug values to every custom Hook. It’s most valuable for custom Hooks that are part of shared libraries.
Defer formatting debug values
In some cases formatting a value for display might be an expensive operation. It’s also unnecessary unless a Hook is actually inspected.
For this reason useDebugValue accepts a formatting function as an optional second parameter. This function is only called if the Hooks are inspected. It receives the debug value as a parameter and should return a formatted display value.
For example a custom Hook that returned a Date value could avoid calling the toDateString function unnecessarily by passing the following formatter:
React.js. Использование хуков. Часть 2 из 3
Хук контекста useContext
Принимает объект контекста (значение, возвращённое из React.createContext ) и возвращает текущее значение контекста для этого контекста. Текущее значение контекста определяется пропом value ближайшего над вызывающим компонентом в дереве.
Хук эффекта useLayoutEffect
Такое поведение useEffect() иногда может вызывать неприятный эффект мерцания для пользователей приложения. Исправить это можно, если использовать хук useLayoutEffect() вместо useEffect() — тогда промежуточное состояние пользователь не увидит. Изменения будут отправлены в браузерный DOM только один раз, после второго рендера.
При запуске приложения мы увидим, как браузер зависает на три секунды, а потом показывает красный квадрат. Хотя рендеров было два, зеленого квадрата мы не видим, изменения в браузерный DOM были отправлены только один раз — после второго рендера.
Хук мемоизации useCallback
После клика по кнопке мы увидим в консоли следующие сообщения:
После клика по кнопке мы увидим в консоли следующие сообщения:
Это прекрасно работает, пока мы не передадим компоненту Child через пропсы callback-функцию:
После клика по кнопке мы увидим в консоли следующие сообщения:
Ну вот, мы и добрались до самого главного. Если обернуть callback-функцию вызовом useCallback() — то useCallback() всегда будет возвращать ссылку на одну и ту же функцию (пока не изменятся зависимости, это второй аргумент хука, подробности здесь).
После клика по кнопке мы увидим в консоли следующие сообщения:
Хук мемоизации useMemo
Хук useMemo() возвращает мемоизированное значение. Принимает в качестве аргуменов «создающую» функцию и массив зависимостей. Позволяет улучшить производительность, когда при каждом ренедере нужно выполнять повторяющиеся сложные вычисления. Если массив зависимостей не был передан, новое значение будет вычисляться при каждом рендере.
Без использования хука useMemo :
Теперь будем использовать хук:
Хук useImperativeHandle
В обычном потоке данных React родительские компоненты могут взаимодействовать с дочерними только через пропсы. Чтобы модифицировать потомка, мы должны заново отрендерить его с новыми пропсами. Тем не менее, могут возникать ситуации, когда требуется императивно изменить дочерний элемент, обойдя обычный поток данных (см. здесь).
Хук состояния useReducer
Редюсер — это шаблон, взятый из Redux. Представляет собой функцию, которая принимает в качестве аргументов предыдущее состояние и требуемое действие, а возвращает — следующее состояние.
Редюсер — «чистая» функция, это значит, что у нее нет побочных эффектов. Она возвращает одно и то же значение, если задать одни и те же аргументы. Такую функцию намного проще тестировать — нет побочных эффектов и она не зависит от React.
Еще один пример использования хука — компонент Color позволяет задать цвет заголовка с помощью шести кнопок, каждая из которых увеличивает или уменьшает значение одной из RGB-составляющей цвета.
Учим useCallback на примерах — React Hooks
Доброго времени суток, друзья. И вновь поговорим про React Hooks. Сегодня мы рассмотрим хук useCallback. В чем его польза? Какие особенности? Давайте обо всем по порядку.
Хук useCallback для мемоизации?
Хук useCallback похож на хук useMemo (больше информации по данному хуку вы можете получить тут) с тем отличием, что он возвращает мемоизированный колбэк, который будет обновлен, только если одна из зависимостей будет изменена.
В примере выше можно увидеть, как происходит процесс мемоизации функции. Достаточно в первый параметр передать функцию, которую требуется мемоизировать, поместив ее в колбек и передав ей зависимые параметры. Вторым параметром useCallback является массив зависимых параметров, благодаря которому будет происходить мемоизация данного колбэка. Если один из переданных параметров изменится, то useCallback вернет новую мемоизированную функцию, при этом, если зависимые параметры не будут изменяться в функциональном компоненте React, то будет использоваться копия данной функции, что позволит оптимизировать применение памяти реактом.
Хук useCallback на практике
Для демонстрации различий в оптимизации кода с помощью хука useCallback и без него, создадим компонент, в который будем передавать функции и поместим вызов этих функций в useEffect для контроля изменений при ее вызове. Это будет означать, что если функция не была изменена (мемоизирована), то ее вызов не будет происходить в этом компоненте.
Поместим данный компонент в App.js, в котором будет храниться состояние двух счетчиков (использую хук useState), также там будут находиться две кнопки, при клике на которые вызываются функции для изменения значения счетчиков.
Самая интересная часть, это две функции, находящиеся в App.js, которые передаются дочернему компоненту Child. Функции будут просто вызывать соnsole.log и их единственное различие будет заключаться в том, что одна из них будет обернута хуком useCallback и иметь в зависимости состояние одного из счетчиков.
Если кликнуть на кнопку “One”, то можно увидеть вызов одновременно двух консолей, при этом, если кликнуть на кнопку “Two”, то будет виден вызов только одной консоли с текстом «Я не мемоизирован». Почему так происходит? Давайте рассмотрим этот процесс подробнее.
По клику на кнопку “One” происходит вызов функции setCounter, в которой мы меняем значение counter c прибавлением к нему единицы. Функция updateTwo мемоизирована и ее новая копия появляется только в момент, когда изменяется значение состояния counter. Как следствие мы получаем нашу обновленную функцию и далее по ссылке в компоненте Child происходит проверка на равенство в useEffect. Ссылка на старую функцию изменилась, что приводит к принудительному перерендереванию дочернего компонента с вызовом самой функции.
При нажатии на кнопку “Two” также происходит изменение счетчика counterTwo на одну единицу. Но данное состояние не указано в массиве зависимостей хука useCallback. Это означает, что функция updateTwo не обновится, а React будет использовать ее закэшированную версию. При дальнейшем сравнении в компоненте Child ссылка на функцию останется прежней, что не приведет к ее вызову.
Заключение
Сегодня мы разобрали на примере счетчик с родительским компонентом и как можно мемоизироивать функции, используя хук useCallback. Использование разработчиками хуков useMemo и useCallback позволяет в значительной степени оптимизировать приложения. Надеюсь, что данный материал был вам полезен. Учитесь, думайте, пишите код. Удачного кодинга, друзья!
Подписывайтесь на наш канал в Telegram и на YouTube для получения самой последней и актуальной информации.
⚛ Демистификация хуков React: useCallback, useMemo и все-все-все
Давайте начнем с объяснения того, что происходит при повторном рендеринге компонента, затем определим назначение useCallback и useMemo, а также когда уместно их использовать.
Что происходит, когда компонент повторно визуализируется?
Другими словами, когда компонент собирается повторно визуализироваться, React сравнивает каждый созданный под его исходным компонентом объект с новой версией этого объекта. И хотя объекты точно такие же, они не указывают на один и тот же объект. React идентифицирует их как разные объекты, позволяя воссоздавать снова и снова при каждом повторном рендеринге.
Для чего нужны useCallback и useMemo?
Рассмотрим их структуру. Оба хука получают два параметра: функцию и массив зависимостей.
useCallback возвращает один и тот же экземпляр передаваемой функции (параметр 1) вместо создания нового при каждом повторном рендеринге компонента. Новый экземпляр передаваемой функции (параметр 1) может быть создан только при изменении массива зависимостей (параметр 2).
Рассмотрим пример: приложение, которое увеличивает первое значение и/или уменьшает второе, а также обновляет итоговый результат. Есть еще третье дополнительное значение, которое пользователь может обновить, однако это число не используется в вычислениях.
Если пользователь взаимодействует с дополнительным значением состояния, компонент будет повторно визуализировать создание новой копии функции additionResult, даже если extraVal в ней не используется. В этом примере мы реализуем useCallback, чтобы создать новую копию функции additionResult при условии, что firstVal или secondVal будут обновлены.
useMemo используется вместо того, чтобы возвращать невызванную функцию, как это делает useCallback – он работает с передаваемой функцией и возвращает результирующее значение только при изменении массива параметров. Другими словами, useMemo вызывает функцию только при необходимости и возвращает кэшированное значение для других визуализаций.
В этом примере мы реализовали приложение, которое принимает число и возвращает его факториал. Например, если передать число 5, программа использовала бы рекурсивную функцию для вычисления: 5! = 5*4*3*2*1 = 120. Здесь мы использовали хук useMemo, чтобы React занимался пересчетом только при изменении числа.
Когда их использовать?
Если вы подумываете о добавлении useCallback и useMemo в ваш компонент, не торопитесь. Они добавляют некоторую дополнительную сложность коду и ухудшают читабельность. Менеджмент производительности с помощью useCallback и useMemo обходится дорого и это не всегда оправдано.
Рассмотрите возможность использования useCallback/useMemo в следующих ситуациях:
Резюме
Когда компонент повторно визуализируется, он создает новые экземпляры всех объектов, включая все функции в нем:
useRef
Почему useRef() – особенный?
Этот хук сохраняет свое состояние между визуализациями компонентов. Магия заключается в его возможности мутировать, не вызывая повторного обновления вашего компонента, поскольку значение useRef существует вне цикла визуализации.
Как использовать useRef()?
Мы инициализируем useRef() и передаем в него начальное значение или инициализируем его пустым, а значения обновляем позже:
useRef() хранит содержащий атрибут current объект, который хранит переданное значение. В нашем примере он будет содержать значение 1.
Для чего его использовать?
Еще одно полезное использование useRef – сохранение предыдущего значения состояния. Рассмотрим пример: есть список из трех покемонов, и вам нужно выбрать любимого. Выберите любой вариант, и вы увидите свой предыдущий выбор внизу. Это возможно в результате использования useRef:
Затем каждый раз, когда мы выбираем другой вариант, он отслеживается в функции changeSelection():
Где обновлять значение useRef()?
Обновление значения ref считается побочным эффектом. Именно по этой причине необходимо обновить значение ref в обработчиках событий и эффектах, а не во время визуализации (если только вы не работаете с ленивой инициализацией). React docs предупреждает нас, что несоблюдение этого правила может привести к неожиданному поведению приложения.
Использовать ли refs вместо state?
Нет. Refs – нереактивный, а значит, изменения значений не приведет к обновлению HTML. Взглянем на следующий пример. Мы инициализировали state, ref и 1000$. Этот компонент позволяет вам тратить эту сумму доллар за долларом каждый раз, когда нажимается кнопка «Spend».
Теперь, если вы потратите хранящиеся в ref средства, они будут вычитаться за каждый клик, но это изменение не вызовет повторного рендеринга – вы не увидите изменений на странице.
Вы можете проверить консоль, чтобы увидеть все значения ref:
useRef() аналог createRef?
Нет, createRef() полезен для доступа к узлам DOM или элементам React, но он создает новый экземпляр ref на каждом рендере вместо того, чтобы сохранять значение между визуализациями при использовании в функциональных компонентах.
useRef() полезен для доступа к узлам DOM или элементам React. Он сохраняет значение даже при повторной визуализации компонента. Вот пример, который позволит увидеть разницу. Мы инициализируем два значения, используя createRef и useRef. Каждый раз, когда нажимается кнопка «Add a render!», обновляется состояние renderCounter и вызывается повторная визуализация, в процессе которой проверяется, являются ли значения refs нулевыми. Если да, то присваиваем ему текущее значение состояния renderCounter.
С другой стороны, созданное с помощью createRef значение ref создается при каждом рендеринге, поэтому оно всегда null, а затем приравнивается к текущему значению состояния renderCounter.
Резюме
useRef() помогает создавать изменяемые переменные внутри функционального компонента, которые не будут обновляться при каждом рендеринге.
useContext
Пробрасывание props-ов vsContext
React предоставляет нам поток данных, в котором родительский компонент использует props для обмена информацией со своими дочерними компонентами. Этот способ отслеживания данных отлично подходит для небольших приложений, однако по мере роста проекта вы можете обнаружить, что происходит проброс props через несколько слоев компонентов. Это называется prop drilling.
Альтернативой prop drilling является использование Context – простого решения, которое дает возможность доступа к данным между компонентами, даже если они не имеют отношений родитель-потомок.
Что такое context object?
C ontext object создается с помощью API createContext() и состоит из двух элементов: провайдер и потребитель. Чтобы создать объект контекста, вы можете инициализировать его пустым или со значением:
Можно также получить доступ к его элементам:
Как использовать провайдер?
Провайдер в context object должен быть обернут вокруг родительского элемента дерева компонентов – это дает каждому компоненту доступ к вашим глобальным данным. Взгляните на теги Prov ider> – они делают состояние name доступным для всех обернутых компонентов. Теперь компоненты и (и любой из их дочерних элементов) имеют доступ к name состояния, даже если мы не передаем name в качестве props.
Как получить доступ к глобальным данным с помощью useContext()?
Как обновить контекст?
Производительность
Использующий useContext() компонент будет повторно визуализироваться при обновлении значения в context object. Вы можете столкнуться с экземпляром, где одно из значений в контексте меняется очень часто, что может привести к повторной визуализации всех использующих useContext() компонентов, даже если быстро меняющееся значение используется только в небольшом дереве компонентов.
Рекомендуемое решение – разделить Context. Поэтому если у вас есть темы Light/Dark и переключатель для их выбора, необходимо создать ThemeContext и AppContext, как показано ниже:
Резюме
Использование context object – отличная альтернатива prop drilling. Он позволяет получить доступ к глобальным данным, не передавая их в качестве props.
useReducer
Что такое reducer?
Они очень полезны, когда необходимо получить одно значение после применения некоторой логики к группе значений. Например, если вы хотите сложить массив чисел для получения общего значения.
reducer – функция, предоставляющая инструкции для получения одного значения. В нашем случае, для суммирования всех заданных значений в массиве nums.
initialValue – начальное значение при реализации инструкций функции reducer. В нашем примере мы определяем начальное значение как 0, поэтому общее возвращаемое значение отражает только сумму значений в массиве nums.
Теперь посмотрим на все это вместе. Метод reduce принимает initialValue и строит его, следуя приведенным в функции reducer инструкциям и добавляя каждое значение в массив nums до тех пор, пока не сможет вернуть одно общее значение.
Что такое useReducer()?
Хук useReducer используется для управления состоянием. В нем есть следующие параметры:
reducer – предоставляющую инструкции по управлению состоянием функция, которая принимает параметры state и action и возвращает новое состояние.
initialState – значение начальной точки. Оно будет меняться в соответствии с инструкциями reducer.
Похоже на описанное ранее поведение функции reduce, но хук useReducer не возвращает только одно значение. Вместо этого он возвращает два элемента в виде массива: текущее состояние и функцию отправки.
Когда следует использовать useReducer?
useReducer является предпочтительной альтернативой useState в следующих случаях:
Резюме
Метод reduce полезен для получения одного значения после применения некоторой логики к группе значений.
Заключение
Мы рассмотрели очень обширную и интересную тему, требующую большой практики и дополнительного изучения. Рекомендуем ознакомиться с официальной документацией и продолжать обучение. Удачи!