Хабрахабр

Революция или боль? Доклад Яндекса о React Hooks

Меня зовут Артём Березин, я разработчик нескольких внутренних сервисов Яндекса. Последние полгода я активно работал с React Hooks. По ходу дела возникали некоторые сложности, с которыми приходилось бороться. Теперь хочу поделиться этим опытом с вами. В докладе я разобрал React Hook API с практической точки зрения — зачем нужны хуки, стоит ли переходить, что лучше учитывать при портировании. В процессе перехода легко наделать ошибок, но избежать их тоже не так сложно.

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

Например — сетевых запросов или запросов к WebSocket: подписка, отписка от каких-то каналов. Прежде всего это поддержка внутреннего состояния и, во вторую очередь, поддержка побочных эффектов. Также хуки дают нам доступ к жизненному циклу компонента, к его началу жизни, то есть монтированию, к обновлению его пропсов и к его смерти. Или, возможно, речь о запросах к каким-то другим асинхронным или синхронным API браузера.

Перед вами здесь самый простой код, который только может быть с компонентом на классах. Наверное, проще всего проиллюстрировать в сравнении. Это обычный счетчик, который можно увеличивать или уменьшать, всего одно поле в state. Компонент что-то меняет. В общем, я думаю, что если вы знакомы с React, код для вас совершенно очевиден.

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

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

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

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

Прежде всего, как следует из предыдущего, используя кастомные хуки, можно шарить логику гораздо проще. Хуки дают некоторые преимущества по сравнению с классами. Теперь мы эту логику закладываем внутрь хуков. Раньше, используя подход с Higher Order Components, мы выкладывали туда какую-то шаренную логику, и она являлась оберткой над компонентом. д. Тем самым дерево компонентов уменьшается: уменьшается его вложенность, и для React становится проще отслеживать изменения компоненты, пересчитывать дерево, пересчитывать виртуальный DOM и т. Тем, кто работает с Redux, я думаю, это хорошо знакомо. Тем самым решается проблема так называемого wrapper-hell.

Дело в том, что нам не нужно сохранять сохранять имена методов, не нужно думать о прототипах. Код, написанный с использованием хуков, гораздо легче поддается минимизации современными минимизаторами типа Terser или старым UglifyJS. Здесь всего этого не нужно делать, поэтому проще минимизировать. После транспиляции, если target стоит ES3 или ES5, у нас обычно появляется куча прототипов, которые патчатся. Для новичков это часто большая проблема и, наверное, одна из главных причин багов: мы забываем про то, что this может быть window, что надо забиндить метод, например, в конструкторе или каким-то еще способом. И, как следствие неиспользования классов, нам не нужно думать про this.

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

Прежде всего, это хуки управления состоянием. Доклад будет неполным, если я не расскажу о каких-то базовых хуках.

В первую очередь — useState.

useState — это функция, которая принимает начальное значение, и возвращает tuple из текущего значения и функции для изменения этого значения. Пример похож на тот, что в начале доклада. Мы же можем просто либо прочитать это значение, либо его изменить. Вся магия обслуживается React внутри.

И эти куски будут друг от друга совершенно изолированы: их можно менять независимо друг от друга. В отличие от классов мы можем использовать столько объектов стейта, сколько нам нужно, разбивает стейт на логические куски, чтобы их не смешивать в одном объекте, как в классах. Запомните этот пример, потому что дальше мы будем делать похожую штуку, но, гораздо более сложную. Результат, например, этого кода: мы меняем две переменные, вычисляем результат и выводим кнопочки, которые позволяют нам изменить первую переменную туда-сюда, и вторую переменную тоже туда-сюда.

Он позволяет более консистентно изменять стейт с помощью редьюсера. Есть такой useState на стероидах для любителей Redux. Я думаю, что те, кто знаком с Redux, можно даже не объяснять, для тех, кто незнаком, расскажу.

Точнее, он передает какие-то параметры, а внутри reducer уже решает, в зависимости от их параметров, как стейт поменяется, и в итоге, должен вернуться новый стейт, обновленный. Редьюсер — это функция, которая принимает стейт, и некий объект, называемый обычно action, который описывает то, как этот стейт должен поменяться.

У нас хук useReducer, он принимает функцию reducer, и вторым параметром начальное значение стейта. Примерно таким образом он используется в коде компонента. Если передать в dispatch объект-action, мы вызовем изменение стейта. Возвращает, так же, как и useState, текущий стейт, и функцию для его изменения — dispatch.

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

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

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

Некоторые хуки — useEffect, useCallback, useMemo — вторым аргументом принимают массив значений, которые позволят сказать, что отслеживать. Здесь мы с вами впервые встречаемся с таким понятием, как dependencies. Например, здесь гипотетически у нас какой-то компонент выбора автора из какого-то списка. Изменения в этом массиве приводят к каким-то эффектам. И при изменении автора, будет вызван useEffect. И табличка с книжками этого автора. При изменении этого authorId будет вызван запрос, и загрузятся книжки.

Иногда удобно для каких-то хаков. Также вскользь упомяну такие хуки, как useRef, это альтернатива React.createRef, что-то похожее на useState, но изменения в ref не приводят к рендеру. Если использовать useRef в родительском компоненте, то он может эти методы дернуть. useImperativeHandle позволяет нам объявить у компонента определенные «публичные методы». useContext — прямо хорошая штука, позволяет взять текущее значение из контекста, если провайдер где-то выше по уровню иерархии определил это значение. Если честно, один раз пробовал в учебных целях, на практике не пригодилось.

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

PureComponent, который отслеживал изменение в пропсах и менял компоненты только тогда, когда изменились пропсы или стейт. Это React.memo, практически альтернатива классу React.

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

Прежде всего, это достаточно низкоуровневая штука — useMemo, редко используется. Внутренние способы оптимизации. Она позволяет вычислять какое-то значение, и перевычислять его только, если указанные в зависимостях значения изменились.

Она, прежде всего, применяется для того, чтобы замемоизировать значение функциий-обработчиков событий, которые будут переданы в дочерние компоненты для того, чтобы эти дочерние компоненты могли не перерендериваться лишний раз. Есть частный случай useMemo для функции, называется useCallback. Мы описываем некую функцию, оборачиваем ее в useCallback, и указываем, от каких переменных она зависит. Используется она просто.

Надо ли нам хуки? У многих возникает вопрос, а надо ли нам это? Тут однозначного ответа нет, все зависит от предпочтений. Переезжаем или остаемся как раньше? В принципе, мне так и показалось, когда я впервые услышал о хуках, что слишком как-то переусложнено, какая-то магия добавляется, и непонятно зачем. В первую очередь, если вы прямо жестко завязаны на объектно-ориентированное программирование, если ваши компоненты, вы привыкли, что это класс, у них есть методы, которые можно дернуть, то, наверное, вам может показаться эта штука лишней.

К примеру, их можно комбинировать или вообще делать что угодно, используя, например, библиотеки, типа Ramda, и тому подобных. Для любителей функциональщины, это, скажем, прямо must have, потому что хуки — это функции, и к ним применимы приемы функционального программирования.

Если использовать эти методы в качестве колбеков. Так как мы избавились от классов, нам теперь нет необходимости привязывать контекст this к методам. Довольно распространенная практика. Обычно, это была проблема, потому что нужно было не забыть забиндить их в конструкторе, либо использовать неофициальное расширение синтаксиса языка, такие arrow-functions в качестве property. Я же использовал свой декоратор, что тоже, в принципе, экспериментально, на методы.

Хуки практически все действия с жизненным циклом связывают с хуком useEffect, который позволяет подписаться, как на рождение, так и на обновление компоненты, так и на его смерть. Есть разница в том, как устроен жизненный цикл, как им управлять. Также метод shouldComponentUpdate теперь можно заменить на React.memo. В классах же для этого приходилось переопределять несколько методов, типа componentDidMount, componentDidUpdate, и componentWillUnmount.

Во-первых, у классов у нас один объект состояния. Есть достаточно небольшая разница в том, как обрабатывается состояние. В хуках же мы можем разбить логическое состояние на какие-то куски, которыми нам удобно было бы оперировать раздельно. Мы должны были запихать туда все, что угодно.

В хуках же мы должны менять весь стейт целиком, и это даже хорошо, потому что модно, что ли, использовать всякие Immutable-штуки и никогда не рассчитывать на то, что у нас объекты могут мутировать. setState() у компонентов на классах позволял указывать патч стейта, тем самым изменяя одно или несколько полей стейта. Они всегда у нас новые.

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

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

Но я советую хотя бы попробовать, хотя бы для нового кода, чтобы прочувствовать. В общем, вы вряд ли получите ответ, переезжать или нет. В принципе, я пытался заменить некоторые возможности, которые у меня были реализованы через Higher Order Components. Когда я только начал работать с хуками, то сразу же выделил несколько кастомных хуков, удобных для себя, для своего проекта.

Это очень удобно, если компонент сложный, если там внутри много каких-то асинхронных операций через Observable, удобно сразу разом от всех отписываться, а не от каждого отдельно. useDismounted — для тех, кто знаком с RxJS, там есть возможность массово отписаться от всех Observable в рамках одного компонента, или в рамках одной функции, передав подписку каждого Observable в специальный объект, Subject, и когда он закрывается, все подписки отменяются.

Похожий хук useBehaviourSubject возвращает из BehaviourSubject. useObservable возвращает значение из Observable, когда оно там появится новое. Его отличие от Observable в том, что у него изначально есть какое-то значение.

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

useWindowResize возвращает текущие актуальные значения для размеров окна. Два похожих хука. Я их использую для пересчета некоторых pop-up или модальных окон, если там какие-то сложные штуки, которые с CSS просто так не делаются. Следующий хук для позиции скролла — useWindowScroll.

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

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

И я писал велосипед. Не так давно мне кинули ссылочку на библиотеку react-use, и оказалось, что большая часть вот этих кастомных хуков там уже реализовано. И вам советую тоже посмотреть, если собираетесь использовать хуки. Это иногда полезно, но в дальнейшем, наверное, скорее всего, я их выкину, и использую react-use.

Самое первое, наверное, с чем сталкивается любой, кто изучает эти хуки и пытается что-то написать, это использовать useEffect неправильно. Собственно, главная цель доклада — показать то, как неправильно писать, какие проблемы могут быть и как их избежать. Он связан с тем, что useEffect поначалу воспринимается ментально, как альтернатива componentDidMount. Здесь тот код, подобный которому 100% каждый писал, если пробовал хуки. И ошибка здесь в том, что он изменяет, допустим, перемену data, и при этом ее изменение приводит к ререндеру компонента, как следствие, эффект будет перезапрошен. Но, в отличие от componentDidMount, который вызывается единожды, useEffect вызывается при каждом рендере. Тем самым мы получаем бесконечную череду AJAX запросов на сервер, и компонент сам себя постоянно обновляет, обновляет, обновляет.

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

Теперь немножко усложнили. Допустим, мы исправили. В этом случае, в принципе, все работает нормально до тех пор, пока мы не изменим entityId в родителе, возможно, для вашего компонента это и не актуально. У нас есть компонент, который рендерит что-то, что нужно взять из сервера по какому-то айдишнику.

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

Здесь, на первый взгляд, все хорошо. Более сложный пример с useCallback. И, например, список хостов, и сверху фильтры, которые позволяют этот список хостов фильтровать. Мы имеем некую страничку, у которой есть какой-то countdown timer, или, наоборот, timer просто, который тикает. Ну, maintenance здесь добавлен просто как для иллюстрации часто изменяющегося значения, который переводит к рирендеру.

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

В нашем случае он ни от чего не зависит, поэтому в конце пустой массив указываем. Здесь обязательно нужно этот onChange просто обернуть в useCallback.

Они, возможно, больше мои. Есть еще некоторые сложности, которые я выделил. Они говорят, что, типа, все хорошо, все о'кей. То есть эти сложности отвергает Facebook, как создатель React. Но все равно некое смущение, что ли, или confusing бывает.

Так как у нас теперь функциональный компонент — это не просто кусок рендера, а есть какая-то логика и, возможно, внутри указана куча всяких функций, то все эти функции и объекты, скорее всего, будут использованы только один раз или редко когда меняться. Что сразу бросается в глаза? Но при этом инициализироваться они будут на каждый рендер.

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

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

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

Иногда, когда мы выносим нашу логику в кастомные хуки, нам хочется эту логику переиспользовать. И забавная такая, даже не претензия, а замечание. В моем случае это был довольно сложный компонент, куда можно было вбивать данные. Но мы не можем это сделать в компонентах старых на классах, потому что они не поддерживают хуки. Он был большой, давно еще написанный на классах. Он искал, его можно было выбирать, типа, dropdown такой сложный. Я хотел эту же логику использовать для этого компонента, который у меня сложный на классах, — не вышло, пришлось его переписывать. Я для другого dropdown сделал pop-up, который автоматом использовал хуки useWindowScroll, useWindowResize и пересчитывал свой размерчик в зависимости от высоты экрана, очень удобно.

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

Я, как и многие в Яндексе, пишу на TypeScript и использую строгую типизацию. Время еще есть, и есть небольшой раздел «Бонус», связанный с хуками. Она заключается в том, что reducer в Redux когда, он принимает объект action. Есть определенная проблема в использовании редьюсеров. И сделать так, чтобы работал автокомплит, и другие плюшки статической типизации, довольно сложно. Так вот, структура этого action может быть совершенно разной, в зависимости от типа action.

Прежде всего, мы объявляем все перечисления со всеми типами action. Я нашел следующий способ. Можем для удобства отладки указать их в виде строк. Можем сделать вот так просто, тогда это будут числа просто, IncrementA это 0, потом 1, 2, и так далее. Потом для каждого типа action мы описываем структуру этого action, расширяя его какими-то полями и обязательно указывая тип. Естественно, они не должны пересекаться, но, в принципе, вам компилятор не даст сделать одинаковые строки. Это первый трюк. Потом создаем UnionType “Action”, где мы перечисляем через вертикальную черту все типы данных, которые туда складываются, какими могут быть action.

Например, здесь есть initialState, просто объект. Второй трюк — вычисление типа на основе значения. Но можно переложить эту задачу на TypeScript. И по идее, надо описать структуру этого объекта еще каким-нибудь одним интерфейсом. Например, здесь я определяю typeState только для чтения, вычисляемый из initialState. Он умеет сам вычислять значения.

Мы передаем State, Action, и дальше происходит магия: мы используем switch по action.type. Дальше эти типы легко использовать в reducer. И мы как раз получаем возможность обращаться к полям этого action нужного нам типа. В TypeScript есть такая фича при использовании UnionType: он может вычислить допустимый тип внутри case, если эти типы отличаются значением какого-то поля, в нашем случае type.

И рефакторинг. Естественно, это удобно: прежде всего, это валидация по типам, подсказки автокомплита и все такое.

В принципе, при использовании особой разницы нет. Как это применять? Здесь мы делаем то же самое, только через reducer. Был предыдущий пример с небольшим калькулятором. Точнее, мы определяем action creator с типами, которые нам нужны, и потом просто отправляем их в dispatch.

Они добавили поддержку хуков и позволяют очень классно показывать все дерево хуков. Еще хочу пропагандировать использование extension официального реактовского Dev Tools. Особенно полезно для кастомных.

Это маленький компонент, у него несложное дерево хуков, но бывают деревья и посложнее. Справа пример, скриншот того, как он это показывает. В данном случае у нас есть хук useConstants, и мы внутри него можем определить какую-то метку, loaded, которая определяет, загрузились ли с сервера константы, например словари. Специальный необходимый для этого хук useDebugValue нужен, чтобы в кастомных хуках можно было добавить какую-нибудь подпись в этот Dev Tool.

Прежде всего, хуки дают довольно большие возможности. В заключение — некоторые выводы. Нужно довольно хорошо понимать, как они работают, особенно то, как они мемоизируют значения. С большими возможностями приходит ответственность, и я призываю вас использовать их с осторожностью. Потому что без мемоизации никуда, мы будем оптимизировать, и тут можно очень сильно себе выстрелить в ногу — мы неправильно что-то замемоизируем и оно будет хранить неактуальные значения, либо наоборот — ререндериться слишком часто.

У Facebook есть специальный плагин для ESLint, который позволяет нам избежать большинства перечисленных проблем. Очень важная рекомендация. Он даже предлагает варианты, как прописать dependencies для хуков. Он отслеживает правильное использование хуков и, прежде всего, помогает. Он их сам может прописать, и это очень удобно, всегда следует обращать внимание, на что там линтер ругается.

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

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

Я про нее узнал уже после того, как доклад был готов, но добавил просто потому, что там мысли совпадают с моими, а это всегда приятно.
— Полное руководство по useEffect. — «React hooks — победа или поражение?» Cтатья очень перекликается с моим докладом. д.
— Статья «useReducer vs useState in React» объясняет, когда нужно использовать useReducer, а когда можно обойтись простым useState. Переводная статья, тоже на Хабре, там прямо досконально по полочкам разложено, как это работает, как его использовать, как не ошибаться и т. Если у вас два-три значения, то обычного useState хватит с головой.
— Сайт React Hooks CheatSheets c множеством интерактивных примеров для изучения хуков.
— Библиотеки кастомных хуков. Спойлер: если у вас сложный стейт, особенно поля, зависимые друг от друга, то лучше использовать useReducer. Там люди выкладывают свои хуки. Usehooks.com — это просто сайт, блог. И я уже упоминал react-use — библиотечку, содержащую полезные для большинства проектов кастомные хуки, которых еще нет из коробки.

Теги
Показать больше

Похожие статьи

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

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

Кнопка «Наверх»
Закрыть