Привет, Хабр! Я Константин Логиновских — разработчик в Cloud.ru. Недавно я выступил на Frontend Talks с темой «Constate — контекст на стероидах». Рассказал, какой контекст захочет использовать каждый, почему Constate — это круто и полезно, а также на примере небольшого приложения показал, как с его помощью улучшить разработку. Мой доклад в письменном виде под капотом — welcome! Context API — это способ передачи данных внутри библиотеки React от родительского компонента к дочернему без использования свойства Props. Его удобно использовать в тех случаях, когда дерево вложенности компонентов достаточно большое и нужно передавать данные напрямую от «родителя» к «внуку», «правнуку» и т. д. В качестве примера напишем небольшое приложение-счетчик с двумя основными компонентами: Сounter (отображает текущее значение счетчика) + кнопка инкремента. Кнопка «Сбросить». Вокруг них будем строить контекст. Напишем простой контекст, который обычно пишут все, кто пользуется контекстом в React. Создаем, типизируем и инициализируем простыми значениями: Затем создаем SimpleCount — компонент всего счетчика. Он будет содержать те значения, которые мы будем передавать дочерним компонентам, а также пробрасывать их в провайдер (чтобы дочерние компоненты Actions и CountView могли его использовать). Через функцию useСontext создаем дочерние компоненты, которые используют контекст — consumers. Они подписываются на определенные значения и их отрисовывают: Итак, мы все написали, контекст работает и данные передаются. Но есть один нюанс: когда нажимаешь на любую кнопку (при работе с контекстом и при изменении значения Number), обновляется слишком много полей. При изменении счетчика почему-то обновилась кнопка «Сбросить»‎, хотя счетчик на эту кнопку не влияет. Давайте разберемся в чем проблема. В терминах React обновление — это перерисовка, то есть выполнение js-кода вокруг того jsx, который мы написали. Ниже код кнопки «Сбросить» — она обновляется, хотя подписана на функцию setNumber, которая не изменилась: Есть 4 причины, по которым может перерисовываться компонент в React: обновление родительского компонента — не подходит т. к. от перерисовки нас защищает функция memo; изменение Props — не подходит т. к. их у нас  нет; изменение State — не подходит т. к. его у нас нет; обновление контекста на который подписан компонент — подходит, но нужно разобраться. Мы подписались только на setNumber, но на самом деле это не совсем так. В коде видно, что при деструктуризации мы подписались не только на setNumber, но и на весь контекст (при этом не используем его). Давайте это исправим — создадим два контекста и в будущем будем подписываться на них. Один из них будет хранить value, а другой функцию изменения этого value. Вносим изменения в SimpleCount. Теперь код стал немного чище: Добавляем дополнительный провайдер и пробрасываем нужные значения: В Actions подписываемся только на функцию setNumber: Отлично, теперь обновляются только нужные компоненты: Теперь представим, что дизайн счетчика решили изменить (обычная практика) и в приложении должно быть уже две кнопки: с необычным инкрементом и «Сбросить». Т. е. теперь мы работаем уже с тремя разными компонентами. Что будем делать: Создаем новый контекст и вносим данные для нового компонента: Используем новый контекст: Затем создаем провайдер, который будет этот контекст использовать: Обновляем логику SimpleСount, добавляя новый провайдер в его рендер, чтобы все заработало: Вроде бы готово, но есть небольшой нюанс — теперь SimpleСount не такой уж Simple. В нем содержится слишком много разной логики и эта логика перемешана с отрисовкой: Чтобы это исправить, создаем новый компонент CounterProvider. Он будет почти такой же как SimpleСount, но заберет на себя всю логику работы со счетчиком. В него будем передавать только дочерние компоненты. Теперь логика отделена. Вот как это выглядит: Но на этом не всё. Проблема в том, что большинство разработчиков не любят так писать и используют сторонние стейт-менеджеры. Почему? Из-за большого количества шаблонного кода (три провайдера, три контекста). Часто для удобства создают дополнительные хуки, которые используют контексты — так не придется импортировать US-контекста и набор-контекст, а можно сразу получить типизированное значение через удобный и семантически понятный хук. Что с этим делать? Использовать стандартную библиотеку Constate — она поможет убрать шаблонный код и сделать работу с контекстом гораздо проще (т. к. берет на себя бесполезную работу). Посмотрим, как Constate поможет в нашем случае. Вынесем логику счетчика в отдельный блок и назовем его useCounter. Это отдельный блок, который будет содержать только нужную логику и возвращать значение, которое мы будем дальше использовать: Теперь возьмем функцию constate и положим в нее useCounter и селекторы: Эти селекторы будут определять именованные хуки — какую именно часть исходного state нам нужно вернуть в результате его исполнения. Таким образом мы: Сократили количество кода в 2 раза. Отделили логику от отрисовки. Получили разделение контекстов: теперь типизацией занимается библиотека. Что еще можно написать с помощью Constate? Все то, для чего раньше вы использовали обычный контекст: модалки (состояние открытости, контент, управление), формы (можно написать простой декоратор final-form, если не хочется его весь тащить), темизацию (любой каприз, который не будет перерисовывать ваш компонент каждый тик), запросы (и такое возможно — маленький стейт со своим запросом, например, конфигурации), составные компоненты (сложные слайдеры со взаимозависимой логикой). Также его можно использовать вместе с react-query в качестве дополнительного стейт-менеджера. Итак, Constate — это очень мощный и гибкий инструмент разработки. Основные преимущества Constate: размер: весит меньше одного КБ (всего 90 строк вместе с документацией и комментариями); универсальность: подходит для разных задач и проектов; автоматизация: контекст можно экспортировать одной строкой, а не вручную. Видео доклада Константина Логиновских на YouTube-канале Cloud.ru Tech. Подписывайтесь! Что ещё интересного есть в блоге: Как освоить Tarantool за 3 месяца и выжить: учимся на ошибках DevOps-инженера On-premise vs облако: на что компаниям выгоднее тратить деньги Создание системы документирования, или как мы от «ворда» к docs as code за месяц переходили Frontend