Что такое react hooks
React hooks — победа или поражение?
С выходом нового React 16.6.0 в документации появился HOOKS (PROPOSAL). Они сейчас доступны в react 17.0.0-alpha и обсуждаются в открытом RFC: React Hooks. Давайте разберемся что это такое и зачем это нужно под катом.
Да это RFC и вы можете повлиять на конечную реализацию обсуждая с создателями react почему они выбрали тот или иной подход.
Давайте взглянем на то как выглядит стандартный хук:
Попробуйте обдумать этот код, это тизер и к концу статьи вы уже будете понимать, что он означает. Первое, что стоит знать, что это не ломает обратную совместимость и возможно их добавят в 16.7 после сбора обратной связи и пожеланий в RFC.
Как уверяют ребята, это не план по выпиливанию классов из реакта.
Так же хуки не заменяют текущие концепции реакта, все на месте props/state/context/refs. Это всего лишь еще один способ использовать их силу.
Мотивация
Хуки решают на первый взгляд не связные проблемы, которые появились при поддержке десятков тысяч компонентов в течении 5 лет у facebook.
Самое сложное это переиспользовать логику в stateful компонентах, у реакта нет способа прикрепить многоразовое поведение к компоненту(например подключить его к хранилищу). Если вы работали с React вам известно понятие HOC(high-order-component) или render props. Это достаточно хорошие паттерны, но иногда они используются чрезмерно, они требуют реструктуризации компонентов, для того, чтобы их можно было использовать, что обычно делает код более громоздким. Стоит посмотреть на типичное реакт приложение и станет понятно о чем идет речь.
Это называется wrapped-hell — ад оберток.
Приложение из одних HOC это нормально в текущих реалиях, подключили компонент к стору/теме/локализации/кастомным хокам, я думаю это всем знакомо.
Становится понятно, что реакту необходим другой примитивный механизм для разделения логики.
Хуки позволяют делать тоже самое разбивая логику между компонентами на маленькие функции и использовать их внутри компонентов.
Классы сложны для людей и для машин
Особенно если не ограничиваться шаблонами, не так давно ребята из реакта эксперементировали с компоновкой компонентов c Prepack и увидели многообещающие результаты, но тем не менее компоненты класса позволяют создавать непреднамеренные плохие паттерны, которые заставляют эти оптимизации исчезать, так же классы не очень хорошо мигрируют и при горячей перезагрузке классы делают ее ненадежной. В первую очередь ребятам хотелось предоставить API которое поддерживает все оптимизации и отлично работает с горячей перезагрузкой.
Глянем на хуки
State hook
Код ниже рендерит параграф и кнопку и если мы нажмем на кнопку то значение в параграфе будет инкрементировано.
таким образом мы создаем сразу несколько состояний и нам не нужно думать о том, чтобы их как то декомпозировать. Таким образом можно выделить, что хуки это функции которые позволяют «подключаться» к фишкам классовых компонентов, так же хуки не работают внутри классов, это важно запомнить.
Effect hook
Часто в классовых компонентах, мы делаем side effect функции, например подписываемся на события или делаем запросы за данными, обычно для этого мы используем методы componentDidMount / componentDidUpdate
Когда мы вызываем useEffect мы говорим реакту сделать ‘side effect’ после обновления изменений в DOM дереве. Эффекты объявляются внутри компонента, поэтому имеют доступ к props/state. Причем их мы можем точно так же создавать сколько угодно.
Сразу же стоит обратить внимание на второй side effect в нем мы возвращаем функцию, делаем мы это для того, чтобы выполнить какие то действия после того как компонент выполняет unmount, в новом api это называют эффекты с очисткой. Остальные эффекты могут возвращать, что угодно.
Правила хуков
Хуки это просто javascript функции, но они требуют всего двух правил:
Кастомные хуки
В тоже время нам хочется переиспользовать логику stateful компонентов, обычно для этого используют либо HOC либо render props паттерны, но они создают дополнительный объем нашего приложения.
Например опишем следующую функцию:
Осознайте этот код, это будет кастомный хук, который мы можем вызывать в различных компонентах. Например так:
В любом случае, мы переиспользуем состояние компонента, каждый вызов функции useFriendStatus создает изолированное состояние. Так же стоит отметить, что начало этой функции начинается со слова use это говорит о том, что это хук. Советуем соблюдать этот формат. Вы можете писать кастомные хуки на что угодно, анимации/подписки/таймеры и много многое другое.
Есть еще пара хуков.
useContext
useContext позволяет использовать вместо renderProps обычное возвращаемое значение, в него следует передать контекст который мы хотим извлечь и он нам его вернет, таким образом мы можем избавиться от всех HOC, которые передавали context в props.
И теперь объект контекста мы можем просто использовать в возвращаемом значении.
useCallback
Как часто вам приходилось создавать компонент класса только для того, чтобы сохранить ссылку на метод? Этого больше не нужно делать, мы можем использовать useCallback и наши компоненты не будут перерисовываться потому что пришла новая ссылка на onClick.
useMemo
Возвращаем мемоизированное значение, мемоизированное значит вычисляется только тогда, когда один из аргументов поменялся, второй раз одно и тоже вычисляться не будет.
Да тут приходится дублировать значения в массиве, чтобы хук понял, что они не изменились.
useRef
useImperativeMethods
useImperativeMethods кастомизирует значение экземпляра который передается из родителя и использует ref напрямую. Как всегда следует избегать передачу ссылок на прямую и следует использовать forwardRef
useMutationEffect
useMutationEffect очень похож на useEffect за исключением того что он запускается синхронно на том этапе когда реакт изменяет значения DOM, прежде чем соседние компоненты будут обновлены, этот хук следует использовать для выполнения DOM мутаций.
Лучше предпочитать useEffect чтобы предотвратить блокировку визуальных изменений.
useLayoutEffect
useLayoutEffect так же похож на useEffect за исключением того, что запускается синхронно после всех обновлений DOM и синхронного ре-рендера. Обновления запланированные в useLayoutEffect применяются синхронно, до того как браузер получит возможность отрисовать элементы. Так же следует стараться использовать стандартный useEffect чтобы не блокировать визуальные изменения.
useReducer
useReducer — это хук для создания редюсера который возвращает состояние и возможность диспатчить изменения:
Так же useReducer принимает 3 аргумент, это action который должен выполнятся при инициализации редюсера:
Так же мы можем создать контекст в данным редюсером и через хук useContext использовать его во всем приложении, это остается на домашнее задание.
Подводя итог
Хуки достаточно мощный подход по решению wrapper-hell и решают несколько проблем, но все их можно свети с одному определению передача ссылок. Уже сейчас начинают появляться сборники хуков по использованию или этот сборник. Более подробнее с хуками можно познакомиться в документации.
Краткий обзор хуков
Хуки — нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.
Хуки — обратно совместимы. На этой странице вы получите общее представление о хуках. Имейте в виду, что это беглый обзор, который больше подойдёт опытным пользователям React. В конце каждого раздела есть вот такой жёлтый блок с детальным объяснением на случай, если вы запутались:
Если вы хотите понять, почему мы добавляем хуки в React, прочтите мотивацию.
Рассмотрим пример, в котором рендерится счётчик. Если вы нажмёте на кнопку, значение счётчика будет инкрементировано.
В этом примере, useState — это хук (определение хука дано ниже). Мы вызываем его, чтобы наделить наш функциональный компонент внутренним состоянием. React будет хранить это состояние между рендерами. Вызов useState возвращает массив с двумя элементами, который содержит: текущее значение состояния и функцию для его обновления. Эту функцию можно использовать где угодно, например, в обработчике событий. Она схожа с this.setState в классах, но не сливает новое и старое состояние вместе. Сравнение хука useState и this.state приводится на странице Использование хука состояния.
Объявление нескольких переменных состояния
Хук состояния можно использовать в компоненте более одного раза.
Хуки — это функции, с помощью которых вы можете «подцепиться» к состоянию и методам жизненного цикла React из функциональных компонентов. Хуки не работают внутри классов — они дают вам возможность использовать React без классов. (Мы не рекомендуем сразу же переписывать существующие компоненты, но при желании, вы можете начать использовать хуки в своих новых компонентах.)
Вам скорее всего доводилось ранее запрашивать данные, делать подписки или вручную менять DOM из React-компонента. Мы расцениваем эти операции как «побочные эффекты» (или сокращённо «эффекты»), так как они могут влиять на работу других компонентов и их нельзя выполнить во время рендера.
К примеру, этот компонент устанавливает заголовок документа после того, как React обновляет DOM:
При необходимости вы можете вернуть из эффекта функцию, которая указывает эффекту, как выполнить за собой «сброс». Например, этот компонент использует эффект, чтобы подписаться на статус друга в сети, и выполняет сброс, отписываясь от него.
Хуки дают вам возможность организовать побочные эффекты в компоненте по связанным частям (например, добавление или отмена подписки), вместо того, чтобы принуждать вас делить всё согласно методам жизненного цикла.
Вы можете узнать больше о useEffect на странице Использование хука эффекта.
Хуки — это функции JavaScript, которые налагают два дополнительных правила:
Мы разработали специальный плагин для линтера, который помогает обеспечивать соблюдение этих правил. Мы понимаем, что эти правила могут показаться немного непонятными и накладывать определённые ограничения, но они очень важны для правильной работы хуков.
Вы можете узнать больше на странице Правила хуков.
💡 Создание собственных хуков
Иногда нужно повторно использовать одинаковую логику состояния в нескольких компонентах. Традиционно использовались два подхода: компоненты высшего порядка и рендер-пропсы. С помощью пользовательских хуков эта задача решается без добавления ненужных компонентов в ваше дерево.
Прежде всего, давайте извлечём эту логику в пользовательский хук useFriendStatus
Хук принимает friendID в качестве аргумента и возвращает переменную, которая показывает, в сети наш друг или нет.
Теперь мы можем использовать этот хук в обоих наших компонентах:
Состояния каждого компонента никаким образом не зависят друг от друга. Хуки — это способ использовать повторно логику состояния, а не само состояние. Более того, каждое обращение к хуку обеспечивает совершенно изолированное состояние. Вы даже можете использовать один и тот же хук несколько раз в одном компоненте.
Пользовательские хуки — это в большей степени соглашение, чем дополнение. Если имя функции начинается с ” use ” и она вызывает другие хуки, мы расцениваем это как пользовательский хук. Если вы будете придерживаться соглашения useSomething при именовании хуков, это позволит нашему плагину для линтера найти баги в коде, который использует хуки.
Есть много подходящих случаев, чтобы написать пользовательские хуки, такие как работа с формами, анимация, декларативные подписки, таймеры и, наверное, много других, о которых мы даже не думали. Мы с нетерпением ожидаем увидеть, какие же пользовательские хуки сообщество React сможет придумать.
А хук useReducer даёт возможность управлять внутренним состоянием более сложного компонента с помощью редюсера.
Вы можете узнать больше обо всех встроенных хуках на странице API-справочника хуков.
Фух, давайте перестанем торопиться и немного охладим пыл! Если вам что-то непонятно или вы хотите узнать о чём-либо более подробно, вы можете начать читать следующие страницы, начиная с документации хука состояния.
Вы также можете просмотреть API-справочник хуков и FAQ хуков.
И наконец, не проходите мимо вступительной страницы, на которой вы узнаете почему мы добавляем хуки и как мы планируем использовать их вместе с классами без необходимости переписывать наши приложения.
React Hooks — что это и почему классы больше не нужны?
В прошлом году React получил глобальное нововведение — hooks, или «хуки». Что это, как ими пользоваться, что будет дальше с React — рассказывает наш фронтенд-разработчик Юрий Громов.
Проблемы классов — причина появления Hooks
Начнём с того, почему React решил отказаться от классовых компонентов.
Во-первых, классы становятся нечитаемыми: они разрастаются и часто хранят разную логику в одном месте. Например, в componentDidMount() можно и задавать стиль для какого-то элемента, и подключаться к сокету, и менять title страницы. И всё это — в одном методе жизненного цикла.
Во-вторых, приходится дополнительно тратить время на оптимизацию классов: проводить рефакторинг, декомпозировать, менять структуру не только конкретного компонента, но и всех связанных с ним. Когда у функционального компонента появляется потребность в методе жизненного цикла, приходится переписывать его на классовый.
В-третьих, использование компонентов высшего порядка усложняет кодовую базу, приходится встраивать их в проект и проводить рефакторинг функционала..
Кстати говоря, классы в React не то чтобы классы — скорее функции. Если вы работали с классами в Java или C#, то знаете, чем классы в этих языках отличаются от классов в React: наследованием, инкапсуляцией и т.д. Всего этого нет у классов React, есть только ключевое слово this и методы.
Что такое хуки?
А зачем нам вообще нужны классы в React? Обычно затем, чтобы работать с локальными состояниями и методами жизненного цикла. Хуки позволяют работать с ними сразу — без написания классов.
Например, хук useState отвечает за состояние. Рассмотрим пример его использования.
const [name, setName] = useState(‘’)
При монтировании компонента в переменной name будет храниться пустая строка, а с помощью функции setName можно изменить это значение. Вызываем setName(‘Андрей’) — и переменная name становится равна ‘Андрей’.
Хук эффекта useEffect отвечает за методы жизненного цикла. В компоненте их может быть несколько, поэтому можно распределить логику в его разные части. Например, можно вызвать 3 хука, аналогичных методу componentDidMount().
Однако самое важное — можно создавать собственные хуки. Это позволяет избежать компонентов высшего порядка. К примеру, вы можете написать хук useAuth: в нём происходит обработка данных и определяется, авторизованы ли вы на сайте. После этого нужно вызвать useAuth внутри компонента, присвоить результат какой-то переменной — и можете дальше спокойно использовать это значение.
Стоит ли переходить с классов на хуки сейчас?
На хуки переходить стоит, хотя бы потому что это уже сделали многие важные крупные библиотеки. Например, React Redux уже имеет useSelector и useDispatch. Этим он здорово облегчил головную боль при работе с ним. Теперь подключаться к хранилищу Redux можно всего одной строчкой, вместо обёртки в виде connect и функций mapStateToProps и mapDispatchToProps.
Что будет с классами?
Классы не исчезнут, как заявляет React. Но, скорее всего, прекратится их поддержка.
Если у вас есть готовые проекты, то при переходе на новую версию React они не должны сломаться. Кроме того, если вы поддерживаете готовые проекты, то можете по ходу переписывать классовые компоненты на функциональные. Классы отлично уживаются с хуками в одном проекте. Есть лишь одно правило — нельзя использовать хук внутри класса.
В целом, React-сообщество довольно появлением хуков. Поэтому, скорее всего, мы будем наблюдать их активное развитие и замещение классов.
Introducing Hooks
Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.
This new function useState is the first “Hook” we’ll learn about, but this example is just a teaser. Don’t worry if it doesn’t make sense yet!
You can start learning Hooks on the next page. On this page, we’ll continue by explaining why we’re adding Hooks to React and how they can help you write great applications.
React 16.8.0 is the first release to support Hooks. When upgrading, don’t forget to update all packages, including React DOM. React Native supports Hooks since the 0.59 release of React Native.
At React Conf 2018, Sophie Alpert and Dan Abramov introduced Hooks, followed by Ryan Florence demonstrating how to refactor an application to use them. Watch the video here:
No Breaking Changes
Before we continue, note that Hooks are:
There are no plans to remove classes from React. You can read more about the gradual adoption strategy for Hooks in the bottom section of this page.
Hooks don’t replace your knowledge of React concepts. Instead, Hooks provide a more direct API to the React concepts you already know: props, state, context, refs, and lifecycle. As we will show later, Hooks also offer a new powerful way to combine them.
If you just want to start learning Hooks, feel free to jump directly to the next page! You can also keep reading this page to learn more about why we’re adding Hooks, and how we’re going to start using them without rewriting our applications.
Hooks solve a wide variety of seemingly unconnected problems in React that we’ve encountered over five years of writing and maintaining tens of thousands of components. Whether you’re learning React, use it daily, or even prefer a different library with a similar component model, you might recognize some of these problems.
It’s hard to reuse stateful logic between components
React doesn’t offer a way to “attach” reusable behavior to a component (for example, connecting it to a store). If you’ve worked with React for a while, you may be familiar with patterns like render props and higher-order components that try to solve this. But these patterns require you to restructure your components when you use them, which can be cumbersome and make code harder to follow. If you look at a typical React application in React DevTools, you will likely find a “wrapper hell” of components surrounded by layers of providers, consumers, higher-order components, render props, and other abstractions. While we could filter them out in DevTools, this points to a deeper underlying problem: React needs a better primitive for sharing stateful logic.
With Hooks, you can extract stateful logic from a component so it can be tested independently and reused. Hooks allow you to reuse stateful logic without changing your component hierarchy. This makes it easy to share Hooks among many components or with the community.
We’ll discuss this more in Building Your Own Hooks.
Complex components become hard to understand
In many cases it’s not possible to break these components into smaller ones because the stateful logic is all over the place. It’s also difficult to test them. This is one of the reasons many people prefer to combine React with a separate state management library. However, that often introduces too much abstraction, requires you to jump between different files, and makes reusing components more difficult.
To solve this, Hooks let you split one component into smaller functions based on what pieces are related (such as setting up a subscription or fetching data), rather than forcing a split based on lifecycle methods. You may also opt into managing the component’s local state with a reducer to make it more predictable.
We’ll discuss this more in Using the Effect Hook.
Classes confuse both people and machines
In addition to making code reuse and code organization more difficult, we’ve found that classes can be a large barrier to learning React. You have to understand how this works in JavaScript, which is very different from how it works in most languages. You have to remember to bind the event handlers. Without unstable syntax proposals, the code is very verbose. People can understand props, state, and top-down data flow perfectly well but still struggle with classes. The distinction between function and class components in React and when to use each one leads to disagreements even between experienced React developers.
Additionally, React has been out for about five years, and we want to make sure it stays relevant in the next five years. As Svelte, Angular, Glimmer, and others show, ahead-of-time compilation of components has a lot of future potential. Especially if it’s not limited to templates. Recently, we’ve been experimenting with component folding using Prepack, and we’ve seen promising early results. However, we found that class components can encourage unintentional patterns that make these optimizations fall back to a slower path. Classes present issues for today’s tools, too. For example, classes don’t minify very well, and they make hot reloading flaky and unreliable. We want to present an API that makes it more likely for code to stay on the optimizable path.
To solve these problems, Hooks let you use more of React’s features without classes. Conceptually, React components have always been closer to functions. Hooks embrace functions, but without sacrificing the practical spirit of React. Hooks provide access to imperative escape hatches and don’t require you to learn complex functional or reactive programming techniques.
Hooks at a Glance is a good place to start learning Hooks.
Gradual Adoption Strategy
TLDR: There are no plans to remove classes from React.
We know that React developers are focused on shipping products and don’t have time to look into every new API that’s being released. Hooks are very new, and it might be better to wait for more examples and tutorials before considering learning or adopting them.
We also understand that the bar for adding a new primitive to React is extremely high. For curious readers, we have prepared a detailed RFC that dives into motivation with more details, and provides extra perspective on the specific design decisions and related prior art.
Crucially, Hooks work side-by-side with existing code so you can adopt them gradually. There is no rush to migrate to Hooks. We recommend avoiding any “big rewrites”, especially for existing, complex class components. It takes a bit of a mind shift to start “thinking in Hooks”. In our experience, it’s best to practice using Hooks in new and non-critical components first, and ensure that everybody on your team feels comfortable with them. After you give Hooks a try, please feel free to send us feedback, positive or negative.
We intend for Hooks to cover all existing use cases for classes, but we will keep supporting class components for the foreseeable future. At Facebook, we have tens of thousands of components written as classes, and we have absolutely no plans to rewrite them. Instead, we are starting to use Hooks in the new code side by side with classes.
Frequently Asked Questions
We’ve prepared a Hooks FAQ page that answers the most common questions about Hooks.
By the end of this page, you should have a rough idea of what problems Hooks are solving, but many details are probably unclear. Don’t worry! Let’s now go to the next page where we start learning about Hooks by example.
Продвинутые React Hooks: подробный разбор useEffect
Feb 22 · 7 min read
С выходом React 16.8 в 2019 году React Hooks наконец-то стали доступны для использования в пригодных для эксплуатации приложениях. Хуки позволяют React-разработчикам делать функциональные компоненты с отслеживанием состояния и не использовать классовые компоненты.
UseEffect — один из трёх больших встроенных React Hooks и один из самых популярных хуков. Он даёт возможность создавать условные изменения, ссылающиеся на состояние программы внутри функционального компонента.
Ближе к концу статьи вы узнаете, как и когда реализовывать этот хук для создания реактивных программ, и поймёте, почему он так часто используется React-разработчиками.
В статье мы рассмотрим следующие вопросы и темы:
Что такое React Hooks?
В React есть фу н кциональные компоненты, которые не содержат внутреннего состояния. А ещё есть классовые компоненты, добавляющие в программу логику с отслеживанием состояния и позволяющие использовать методы жизненного цикла.
Многие разработчики были против такого подхода, так как классовым компонентам для поддержки внутренних состояний требуются классы ES6.
И вот была предложена альтернатива в виде React Hooks.
React Hooks — это функции, которые позволяют подцепиться к состоянию и жизненному циклу React из функциональных компонентов. Это даёт возможность использовать React без классов, которые многим не нравятся из-за их зависимости от вызовов this JavaScript. А главное — хуки включаются по желанию и работают с имеющимся кодом.
Существует несколько встроенных хуков (таких как useEffect или useState ), которые ссылаются на стандартные внутренние состояния. Есть также возможность создавать пользовательские хуки, ссылающиеся на выбранные состояния.
Вот самые популярные встроенные хуки:
Преимущества React Hooks:
Сравнение реализации компонентов с классами и хуками
Хуки предназначены для всего того, на что способны классы, и могут даже больше. Посмотрим, как преобразится старый код на React с использованием хуков вместо классов.
Вот старый код на React без хуков:
Для этого внесём в код следующие изменения:
Вот как теперь выглядит то же самое приложение на React с хуками:
Хуки легко задействовать в приложении, и код при этом становится более удобным для восприятия!
Что представляет собой хук useEffect?
useEffect — один из самых популярных хуков, ведь он выполняет побочные эффекты в функциональных компонентах. Присмотримся к нему повнимательнее, чтобы понять, как это происходит.
Хук useEffect позволяет запускать дополнительный код после того, как React обновит DOM.
Синтаксис
Хук useEffect принимает два аргумента:
Первый аргумент — это функция обратного вызова, которая по умолчанию запускается после каждого отображения.
Второй аргумент — это опциональный массив зависимостей, который указывает хуку на выполнение обратного вызова только при наличии изменения в целевом состоянии. Хук сравнивает значение предыдущего и текущего состояния каждой зависимости. Если эти два значения не совпадают, хук использует обратный вызов первого аргумента.
Массивы зависимостей переопределяют поведение обратного вызова по умолчанию и обеспечивают, что хук проигнорирует всё остальное в области компонента.
Примеры использования
Вот типичные сценарии применения useEffect :
В каждом из этих случаев useEffect используется вместо метода жизненного цикла.
Использование массива зависимостей с хуком useEffect Hook
Для оптимизации хука useEffect необходимо правильно задействовать массивы зависимостей. Важно использовать эти хуки для предотвращения ненужных повторных отображений, даже когда ничего не меняется.
Приведённый ниже код выводит на страницу полученное сообщение, но не использует массив зависимостей.
Всё вроде хорошо, но при открытии консоли браузера обнаруживается, что сообщение >> Loading Message несколько раз перезапускалось.
Сообщение не изменилось, поэтому оптимизируем всё это: сообщения будут загружаться и получаться только раз.
Секрет в добавлении пустого массива зависимостей. Строки 8–10 просто заменяются на:
По умолчанию хук useEffect запускается после каждого повторного отображения. А с массивом зависимостей он запускается один раз и затем запускается снова при каждом изменении передаваемой зависимости. Пустой массив не оставляет условий для повторного запуска хука, обеспечивая получение сообщения только при первом отображении.
Запуск функции useEffect с изменением состояния или пропсов
Массивы зависимостей также полезны при создании адаптивных приложений. Но это должны быть заполненные массивы.
Возьмём приложение на React, позволяющее пользователям устанавливать псевдоним, вводя его в поле ввода. После установки псевдонима приложение получает персонализированное приветственное сообщение из внешнего API.