Useselector react redux что это
Hooks
React’s new «hooks» APIs give function components the ability to use local component state, execute side effects, and more. React also lets us write custom hooks, which let us extract reusable hooks to add our own behavior on top of React’s built-in hooks.
React Redux includes its own custom hook APIs, which allow your React components to subscribe to the Redux store and dispatch actions.
We recommend using the React-Redux hooks API as the default approach in your React components.
The existing connect API still works and will continue to be supported, but the hooks API is simpler and works better with TypeScript.
These hooks were first added in v7.1.0.
Using Hooks in a React Redux App
component to make the store available throughout the component tree:
From there, you may import any of the listed React Redux hooks APIs and use them within your function components.
useSelector()
Allows you to extract data from the Redux store state, using a selector function.
The selector function should be pure since it is potentially executed multiple times and at arbitrary points in time.
The selector is approximately equivalent to the mapStateToProps argument to connect conceptually. The selector will be called with the entire Redux store state as its only argument. The selector will be run whenever the function component renders (unless its reference hasn’t changed since a previous render of the component so that a cached result can be returned by the hook without re-running the selector). useSelector() will also subscribe to the Redux store, and run your selector whenever an action is dispatched.
However, there are some differences between the selectors passed to useSelector() and a mapState function:
There are potential edge cases with using props in selectors that may cause issues. See the Usage Warnings section of this page for further details.
You may call useSelector() multiple times within a single function component. Each call to useSelector() creates an individual subscription to the Redux store. Because of the React update batching behavior used in React Redux v7, a dispatched action that causes multiple useSelector() s in the same component to return new values should only result in a single re-render.
Equality Comparisons and Updates
When the function component renders, the provided selector function will be called and its result will be returned from the useSelector() hook. (A cached result may be returned by the hook without re-running the selector if it’s the same function reference as on a previous render of the component.)
The optional comparison function also enables using something like Lodash’s _.isEqual() or Immutable.js’s comparison capabilities.
useSelector Examples
Using props via closure to determine what to extract:
Using memoizing selectors
When using useSelector with an inline selector as shown above, a new instance of the selector is created whenever the component is rendered. This works as long as the selector does not maintain any state. However, memoizing selectors (e.g. created via createSelector from reselect ) do have internal state, and therefore care must be taken when using them. Below you can find typical usage scenarios for memoizing selectors.
When the selector does only depend on the state, simply ensure that it is declared outside of the component so that the same selector instance is used for each render:
The same is true if the selector depends on the component’s props, but will only ever be used in a single instance of a single component:
However, when the selector is used in multiple component instances and depends on the component’s props, you need to ensure that each component instance gets its own selector instance (see here for a more thorough explanation of why this is necessary):
useDispatch()
This hook returns a reference to the dispatch function from the Redux store. You may use it to dispatch actions as needed.
Examples
The dispatch function reference will be stable as long as the same store instance is being passed to the
. Normally, that store instance never changes in an application.
useStore()
This hook returns a reference to the same Redux store that was passed in to the
This hook should probably not be used frequently. Prefer useSelector() as your primary choice. However, this may be useful for less common scenarios that do require access to the store, such as replacing reducers.
Examples
Custom context
component allows you to specify an alternate context via the context prop. This is useful if you’re building a complex reusable component, and you don’t want your store to collide with any Redux store your consumers’ applications might use.
To access an alternate context via the hooks API, use the hook creator functions:
Usage Warnings
Stale Props and «Zombie Children»
The React-Redux hooks API has been production-ready since we released it in v7.1.0, and we recommend using the hooks API as the default approach in your components. However, there are a couple of edge cases that can occur, and we’re documenting those so that you can be aware of them.
Specifically, «stale props» means any case where:
Depending on what props were used and what the current store state is, this may result in incorrect data being returned from the selector, or even an error being thrown.
«Zombie child» refers specifically to the case where:
useSelector() tries to deal with this by catching all errors that are thrown when the selector is executed due to a store update (but not when it is executed during rendering). When an error occurs, the component will be forced to render, at which point the selector is executed again. This works as long as the selector is a pure function and you do not depend on the selector throwing errors.
If you prefer to deal with this issue yourself, here are some possible options for avoiding these problems altogether with useSelector() :
For a longer description of these scenarios, see:
Performance
If further performance optimizations are necessary, you may consider wrapping your function component in React.memo() :
Hooks Recipes
We’ve pared down our hooks API from the original alpha release, focusing on a more minimal set of API primitives. However, you may still wish to use some of the approaches we tried in your own apps. These examples should be ready to copy and paste into your own codebase.
Recipe: useActions()
However, if you’d like to still use this hook yourself, here’s a copy-pastable version that supports passing in action creators as a single function, an array, or an object.
Recipe: useShallowEqualSelector()
Additional considerations when using hooks
There are some architectural trade offs to take into consideration when deciding whether to use hooks or not. Mark Erikson summarizes these nicely in his two blog posts Thoughts on React Hooks, Redux, and Separation of Concerns and Hooks, HOCs, and Tradeoffs.
React: основные подходы к управлению состоянием
Доброго времени суток, друзья!
Предлагаю вашему вниманию простое приложение — список задач. Что в нем особенного, спросите вы. Дело в том, что я попытался реализовать одну и ту же «тудушку» с использованием четырех разных подходов к управлению состоянием в React-приложениях: useState, useContext + useReducer, Redux Toolkit и Recoil.
Начнем с того, что такое состояние приложения, и почему так важен выбор правильного инструмента для работы с ним.
Состояние — это собирательное понятие для любой информации, имеющей отношение к приложению. Это могут быть как данные, используемые в приложении, такие как тот же список задач или список пользователей, так и состояние как таковое, например, состояние загрузки или состояние формы.
Условно, состояние можно разделить на локальное и глобальное. Под локальным состоянием, обычно, понимается состояние отдельно взятого компонента, например, состояние формы, как правило, является локальным состоянием соответствующего компонента. В свою очередь, глобальное состояние правильнее именовать распределенным или совместно используемым, подразумевая под этим то, что такое состояние используется более чем одним компонентом. Условность рассматриваемой градации выражается в том, что локальное состояние вполне может использоваться несколькими компонентами (например, состояние, определенное с помощью useState(), может в виде пропов передаваться дочерним компонентам), а глобальное состояние не обязательно используется всеми компонентами приложения (например, в Redux, где имеется одно хранилище для состояния всего приложения, обычно, создается отдельный срез (slice) состояния для каждой части UI, точнее, для логики управления этой частью).
Важность выбора правильного инструмента для управления состоянием приложения обусловлена теми проблемами, которые возникают при несоответствии инструмента размерам приложения или сложности реализуемой в нем логики. Мы убедимся в этом в процессе разработки списка задач.
Я не буду вдаваться в подробности работы каждого инструмента, а ограничусь общим описанием и ссылками на соответствующие материалы. Для прототипирования UI будет использоваться react-bootstrap.
Создаем проект с помощью Create React App:
useState()
Хук «useState()» предназначен для управления локальным состоянием компонента. Он возвращает массив с двумя элементами: текущим значением состояния и сеттером — функцией для обновления этого значения. Сигнатура данного хука:
Пока ограничимся четырьмя базовыми операциями: добавление, переключение (выполнение), обновление и удаление задачи, но усложним себе жизнь тем, что наше начальное состояние будет иметь форму нормализованных данных (это позволит как следует попрактиковаться в иммутабельном обновлении).
Думаю, тут все понятно.
В App.js мы с помощью useState() определяем начальное состояние приложения, импортируем и рендерим компоненты приложения, передавая им состояние и сеттер в виде пропов:
В TodoForm.js мы реализуем добавление новой задачи в список:
В TodoList.js мы просто рендерим список элементов:
Наконец, в TodoListItem.js происходит самое интересное — здесь мы реализуем оставшиеся операции: переключение, обновление и удаление задачи:
Заключение
Итак, мы с вами реализовали список задач с использованием четырех разных подходов к управлению состоянием. Какие выводы можно из всего этого сделать?
Я выскажу свое мнение, оно не претендует на статус истины в последней инстанции. Разумеется, выбор правильного инструмента для управления состоянием зависит от задач, решаемых приложением:
Готовим селекторы в Redux
Зачем нужны селекторы?
Чтобы ответить на этот вопрос, нужно разобраться в том, что вообще из себя представляет редакс.
Собственно по этим причинам, там и напрашивается какой-то отдельный слой, который возьмет на себя роль получения, комбинирования и преобразования данных. Этим слоем стали селекторы.
Основные правила использования селекторов
Чтобы извлечь максимальную пользу от селекторов, нужно соблюдать несколько правил.
1. Не писать функции-селекторы прямо в компонентах
Почему?
Логика получения данных из структуры стора находится внутри компонента.
Но зачем компоненту знать об этом?
Ну и это просто-напросто неудобно.
И что делать?
Смотреть следующий пункт =)
2. Выносить селекторы в отдельный слой
Вы можете располагать этот слой в различных местах, в зависимости от используемой вами архитектуры. Но это всегда место, близкое к слайсу (редьюсеру, экшнам).
Также важно, чтобы селекторы были в отдельном файле, а не там же, где например редьюсер и прочие нерелевантные к извлечению данных вещи.
Сам файл может выглядеть так:
Зачем это нужно?
Когда селекторы расположены в одном месте, мы получаем сразу массу преимуществ.
Во-вторых, селекторы становятся переиспользуемыми и инкапсулируют в себе определенную логику. Плюсы можно увидеть уже в примере выше: nextCount и prevCount уже не знают о том, как работает count и не зависят от местоположения этой части данных в сторе. А внешний код тем более не должен знать детали получения каких-то данных, особенно если стор имеет специфичную структуру.
3. Группировать селекторы в один объект (namespace)
Теперь, при использовании во внешнем коде, мы можем просто написать следующее:
Зачем это нужно?
Помимо того, что такой namespace позволяет нам без потери семантики полностью уйти от каких-либо префиксов в функциях, он так же избавляет нас от потенциальных конфликтов между именами селекторов в различных модулях.
Все эти проблемы решает объединение в namespace. И это довольно важный пункт.
4. Не использовать мемоизацию, когда она не нужна
Иногда мемоизированные селекторы только усложнят код, замедлят его, или будут расходовать лишнюю память.
Виды селекторов
Мемоизированные селекторы, созданные через createSelector из reselect
Селекторы без мемоизации, в которых мы ручками принимаем state и возвращаем нужные данные
Мемоизированные селекторы
В редаксе нельзя подписаться на изменение конкретного кусочка данных. Изначально, можно лишь узнать о том, что «где-то что-то изменилось».
Именно из этого факта вытекает необходимость в мемоизированных селекторах.
Тяжелые вычисления
Вы же не хотите, чтобы тяжелые преобразования выполнялись на каждое изменение в сторе? Например, для интернет-магазина, что насчет пересчитать корзину покупок после того, как пользователь открыл какую-нибудь модалку, состояние которой хранится глобально в редаксе?
Преобразование данных и композиция
Из-за проблемы редакса, которая была описана выше, селектор вызовется даже тогда, когда изменившиеся данные не имеют к нему никакого отношения.
Проблемы начинаются тогда, когда от данного объекта начинают зависеть компоненты, хуки, и другие селекторы.
Почти везде сравнение происходит по ссылке:
useSelector и connect увидят, что результат изменился, и перерендерят компонент. И не важно, что содержимое объекта один в один равно предыдущему.
Поэтому, используем мемоизированный селектор:
Возможен и другой случай:
А вот то же самое обычным селектором:
Мне кажется, такой селектор не так приятно выглядит и при увеличении зависимостей быстро приходит в не очень читабельное состояние. А если попытаться исправить это, мы столкнемся с конфликтами между именами селекторов и переменных.
Обычные селекторы
Мемоизированные селекторы в подобных случаях использовать не стоит — из-за проверки входных данных и сравнения их с предыдущими, такие селекторы будут медленнее (
в 30 раз в последнем Chrome), а объем занимаемой памяти увеличится, так как предыдущие входные данные нужно где-то хранить. Проблема с памятью не очень заметна, но становится вполне ощутима, когда входными данными является объект с множеством данных.
Многие любят создавать селекторы, принимающие пропсы.
Например, так можно вынести из компонента получение конкретного юзера по ID.
Упс, это что, создание нового селектора в компоненте?
Да, выше я писал о том, что не нужно так делать.
Но конкретно в этом случае я не нашел другого выхода.
Немного о useSelector
Может показаться, что useSelector позволяет не использовать мемоизированные селекторы, но это не так.
Заключение
Соблюдайте основные правила
Выносите селекторы в отдельный слой (например на уровне модуля) и далее используйте их в остальных участках приложения.
Объединяйте селекторы в объект (namespace), чтобы избежать повторений кода и конфликтов имен. Лучше всего для этого подходит ре-экспорт.
Используйте обычные селекторы без мемоизации когда:
Нужно просто достать значение из стора.
(не обязательно) Нужно сделать простую операцию между какими-то значениями, при этом результатом этой операции является примитив.
Используйте мемоизированные селекторы когда:
В селекторе есть тяжелые вычисления (фильтрация, сортировка, сложное преобразование данных, и так далее).
Результатом вызова селектора является объект. Ну и конечно же, это касается массивов и различных структур вроде Set и Map, так как они тоже являются объектами.
Используйте re-reselect когда:
Стандартный кэш из reselect не справляется
Изучайте интересные вещи
Заменяют ли хуки в React Redux?
С тех пор, как в React появились хуки, возникает много вопросов о том, способны ли они заменить Redux.
Я полагаю, что хуки и Redux имеют мало общего между собой. Хуки не дают нам неких новых удивительных возможностей по работе с состоянием. Они, вместо этого, расширяют API, позволяющие делать в React то, что в нём уже было возможным. Однако API хуков сделал гораздо удобнее работу со встроенными возможностями React по управлению состоянием. Оказалось, что новыми возможностями по работе с состоянием пользоваться легче, чем старыми, которые имелись в компонентах, основанных на классах. Теперь я гораздо чаще, чем раньше, использую средства по работе с состоянием компонентов. Естественно, поступаю я так лишь тогда, когда это уместно.
Для того чтобы объяснить моё отношение к хукам React и к Redux, мне хотелось бы сначала рассказать о том, в каких ситуациях обычно прибегают к использованию Redux.
Что такое Redux?
Redux — это библиотека, реализующая предсказуемое хранилище состояния приложений. Это, кроме того, архитектура, которая легко интегрируется с React.
Вот основные сильные стороны Redux:
Что такое хуки React?
Хуки React позволяют, при работе с функциональными компонентами, пользоваться аналогом состояния компонентов, основанных на классах, и аналогами методов их жизненного цикла. Хуки появились в React 16.8.
Среди основных сильных сторон хуков можно отметить следующие:
Если пользоваться инструментами наподобие API react-redux с поддержкой хуков, или хуком React useReducer, окажется, что нет причины задавать вопрос о том, что выбрать — хуки или Redux. Можно пользоваться и тем и другим, сочетая и комбинируя эти технологии.
Что заменяют хуки?
После появления API хуков я перестал использовать следующие технологии:
Что не заменяют хуки?
Я всё ещё часто пользуюсь следующими технологиями:
Когда стоит пользоваться хуками?
Нет нужды стремиться к тому, чтобы использовать Redux в каждом приложении и в каждом компоненте. Если ваш проект состоит из одного визуального компонента, если он не сохраняет данные в состояние и не загружает их оттуда, если в нём не выполняются асинхронные операции ввода-вывода, то я не могу придумать достойной причины усложнять этот проект за счёт использования в нём Redux.
То же самое можно сказать и о компонентах, обладающих следующими особенностями:
Здесь useState используется для управления кратковременно используемым состоянием полей ввода name и email :
Использование локального состояния компонента для решения подобных задач всегда выглядело хорошо, но до появления хуков React у меня бы, в любом случае, возникло бы желание сохранять данные компонента в Redux-хранилище и получать состояние из свойств.
Раньше работа с состоянием компонента предусматривала использование компонентов, основанных на классах, запись начальных данных в состояние с применением механизмов объявления свойств класса (или в конструкторе класса) и так далее. В результате оказывалось, что для того, чтобы избежать использования Redux, компонент приходилось слишком сильно усложнять. В пользу Redux говорило и существование удобных инструментов для управления состоянием форм с помощью Redux. В результате раньше я не беспокоился бы о том, что временное состояние формы хранится там же, где и данные, имеющие более длительный срок существования.
Так как я уже использовал Redux во всех моих мало-мальски сложных приложениях, выбор технологии для хранения состояния компонентов особых раздумий у меня не вызывал. Я просто пользовался Redux практически во всех случаях.
В современных условиях делать выбор тоже несложно: работа с состоянием компонента организуется с помощью стандартных механизмов React, а управление состоянием приложения — с помощью Redux.
Когда стоит пользоваться Redux?
Ещё один распространённый вопрос, касающийся управления состоянием, звучит так: «Нужно ли мне помещать абсолютно всё в хранилище Redux? Если я этого не сделаю, не нарушит ли это возможности по отладке приложений с использованием механизмов TTD?».
Нет нужды размещать абсолютно всё в хранилище Redux. Дело в том, что в приложениях используется много временных данных, которые слишком сильно по нему разбросаны для того, чтобы дать какую-то информацию, которая, будучи записанной в журнал или применённой при отладке, может оказать разработчику заметную помощь в поиске проблем. Вероятно, вам, если только вы не пишете приложение-редактор, работающее в реальном времени, нет нужды записывать в состояние каждое движение мыши или каждое нажатие на клавишу клавиатуры. Когда вы помещаете нечто в состояние Redux, вы добавляете в приложение дополнительный уровень абстракции, а также — сопутствующий ему дополнительный уровень сложности.
Другими словами, вы можете спокойно пользоваться Redux, но на это у вас должны быть некие причины. Применение возможностей Redux в компонентах может оказаться оправданным в том случае, если компоненты отличаются следующими особенностями:
Этот документ не занимается работой с DOM. Это — презентационный компонент. Он подключён к Redux с использованием API react-redux с поддержкой хуков.
Redux здесь используется из-за того, что нам нужно, чтобы данные, которыми занимается эта форма, можно было бы использовать и в других частях пользовательского интерфейса. А после того, как операция покупки будет завершена, нам надо сохранить соответствующую информацию в базе данных.
Фрагменты состояния, с которыми работает этот код, используются различными компонентами, они не обрабатываются лишь одним компонентом. Это — не данные, существующие лишь небольшой отрезок времени. Эти данные можно считать постоянными, они вполне могут использоваться на разных экранах приложения и в нескольких сессиях. Всё это — сценарии, в которых состояния компонента для хранения данных применить не удастся. Это, правда, всё же возможно, но только в том случае, если создатель приложения напишет, на базе API React, собственную библиотеку для управления состоянием. Подобное сделать гораздо сложнее, чем просто воспользоваться Redux.
API React Suspense, в будущем, может пригодиться при выполнении сохранения данных в состоянии и загрузки их из него. Нам нужно подождать его выхода и посмотреть — сможет ли оно заменить шаблоны сохранения и загрузки данных Redux. Redux позволяет нам чётко отделять побочные эффекты от остальной логики компонента, при этом нам не нужно особым образом работать со службами ввода-вывода. (Причиной того, что я предпочитаю библиотеку redux-saga промежуточному ПО redux-thunk, является изоляция эффектов). Для того чтобы соревноваться в этом сценарии с Redux, API React понадобится обеспечить изоляцию эффектов.
Redux — это архитектура
Redux — это гораздо больше (а часто — и гораздо меньше), чем библиотека для управления состоянием. Это ещё и подмножество архитектуры Flux, которая гораздо более жёстко определяет то, как выполняются изменения состояния. Подробности об архитектуре Redux можно почитать здесь.
Я часто использую редьюсеры, созданные в стиле Redux, в тех случаях, когда мне нужно поддерживать сложное состояние компонента, но не нужно пользоваться библиотекой Redux. Я, кроме того, пользуюсь действиями, созданными в духе Redux (и даже Redux-инструментами вроде Autodux и redux-saga) для отправки действий в Node.js-приложениях. При этом я даже не импортирую в подобные приложения Redux.
Проект Redux всегда был больше архитектурой и набором добровольно соблюдаемых соглашений, чем библиотекой. На самом деле, базовую реализацию Redux можно уложить буквально в пару десятков строк кода.
Это окажется хорошей новостью для тех, кому хочется чаще использовать локальные состояния компонентов с помощью хуков и при этом не привязывать всё к Redux.
Если позже вам понадобится наладить постоянное хранение данных, которые раньше вы хранили лишь временно, то вы будете на 90% готовы к подобному изменению. Всё, что вам останется сделать — это подключить компонент к хранилищу Redux и добавить туда соответствующий редьюсер.
Вопросы и ответы
▍Нарушается ли детерминизм в том случае, если Redux управляет не всеми данными приложения?
Нет, не нарушается. На самом деле, использование Redux не делает проект детерминированным. А вот соглашения — делают. Если вы хотите, чтобы ваше Redux-состояние было бы детерминированным — используйте чистые функции. То же самое касается и ситуаций, в которых нужно, чтобы детерминированным бы было временное состояние локальных компонентов.
▍Должна ли библиотека Redux играть роль единого источника достоверных данных?
Принцип единого источника достоверных данных не указывает на то, что нужно, чтобы все данные, входящие в состояние приложения, хранились бы в одном месте. Смысл этого принципа заключается в том, что у каждого фрагмента состояния должен быть лишь один источник достоверных данных. В результате у нас может быть множество фрагментов состояния, у каждого из которых есть собственный источник достоверных данных.
Это означает, что программист может принять решение о том, что передаётся в Redux, и о том, что передаётся в состояние компонентов. Данные, определяющие состояние, можно брать и из других источников. Например — из браузерного API, позволяющего работать со сведениями об адресе просматриваемой страницы.
Redux — это отличный инструмент для поддержки единого источника достоверных данных для состояния приложения. Но если состояние компонента находится и используется исключительно в пределах этого компонента, то, по определению, у этого состояния уже имеется единственный источник достоверных данных — состояние компонента React.
Если вы помещаете некие данные в состояние Redux, вы всегда должны выполнять чтение этих данных из состояния Redux. Для всего, что находится в хранилище Redux, это хранилище должно выступать единственным источником достоверных данных.
Если нужно, то помещать всё в состояние Redux — это совершенно нормально. Возможно, это повлияет на производительность в случае использования фрагментов состояния, которые нужно часто обновлять, или в том случае, если речь идёт о хранении состояния компонента, в котором интенсивно используются зависимые фрагменты состояния. О производительности не стоит беспокоиться до тех пор, пока с производительностью не возникнут проблемы. Но если вас вопрос производительности беспокоит — попробуйте оба способа работы с состоянием и оцените их воздействие на производительность. Выполните профилирование проекта и помните о модели производительности RAIL.
▍Нужно ли мне использовать функцию connect из react-redux, или лучше использовать хуки?
Это зависит от многого. Функция connect создаёт компонент высшего порядка, подходящий для многократного использования, а хуки оптимизированы для интеграции с отдельным компонентом.
Теперь, если с приложением интенсивно работает администратор, все действия которого нуждаются в особом разрешении, вы можете создать компонент высшего порядка, который сочетает в себе все эти разрешения со всей необходимой сквозной функциональностью:
Вот как это использовать:
Другими словами можно сказать, что я полагаю, что при разработке connect была проведена большая работа в направлении лаконичности кода, но получающийся код не оказывается ни особенно хорошо читаемым, ни особенно удобным. Если мне не нужно работать с несколькими компонентами, то я с удовольствием предпочту неудобному API connect гораздо более удобный API хуков, даже учитывая то, что это приведёт к росту объёма кода.
▍Если синглтон считается анти-паттерном, а Redux является синглтоном, значит ли это, что Redux — это анти-паттерн?
Нет, не значит. Использование в коде паттерна синглтон намекает на сомнительное качество этого кода, указывая на наличие в нём совместно используемого изменяемого состояния. Вот это — настоящий анти-паттерн. Redux же предотвращает мутацию разделяемого состояния через инкапсуляцию (не следует менять состояние приложения напрямую, за пределами редьюсеров; задачи по изменению состояния решает Redux) и через передачу сообщений (изменение состояния может вызвать лишь отправленный объект события).
Итоги
Заменяют ли Redux хуки React? Хуки — это замечательно, но Redux они не заменяют.
Надеемся, что этот материал поможет вам в деле выбора модели управления состоянием для ваших React-проектов.
Уважаемые читатели! Встречались ли вы с ситуациями, в которых хуки React способны заменить Redux?