В редаксе действительно 1 стор, но состоит он из стольких «подсторов» с редюсерами, сколько нужно приложению:
import { combineReducers } from "redux";
import store1 from "./store1";
import store2 from "./store2";
// + 100500 других сторов
export default combineReducers({
store1,
store2,
});
В MobX тоже можно комбинировать «подсторы»:
import { Store1 } from './Store1 ';
import { Store2 } from './Store2 ';
export class StoreRoot {
constructor() {
this.store1 = new Store1();
this.store2 = new Store2();
}
}
То есть по способу компоновки сторов очень похоже. Вы, наверное, имеете в виду ситуацию, когда Store1 должен обратиться к Store2? Это решается несложно. А в редаксе другая схема работы — в нем сторы вообще не могут общаться, они просто содержат данные, а экшены могут обращаться с помощью глобальных констант к любому стору. Можно сделать такую же схему и на MobX, он никак не ограничивает возможности.
При работе с асинхронно дозагружаемыми компонентами и их внутренним состоянием я предпочитаю мерджить его в глобальный стейт для универсальной схемы работы. То, что при загрузке 5 страниц (каждая из них — условно отдельный фронт) у меня будет единый стейт, в котором хранится их состояние, вместо 6 разных стейтов (на каждую страницу + рут) — это большое преимущество.
Вообще я как раз про кастомные хуки говорил) Бывает длиннющая цепочка из полусотни, кроме шуток, и «плоско» — это только на верхнем уровне, то есть в функции рендера, в то время как если распутать весь клубок, получится множество взаимосвязей и изменений одних и тех же параметров.
context hell — это наверное при использовании их в иерархичном подходе вместо глобального? Тогда поддерживаю, это нелепость. Функционал разный — wysiwig, тултипы, селекты, графики и системы из них, сложные галереи с динамическими раскладками, таблицы со всякими сортировками-дозагрузками, календари ну и что там еще фронтенд-разработчикам прилетает. Стараюсь вникать в сложные проекты, так как и оплата там соответствующая, поэтому от недостатка количества функционала не страдаю — бывают документации по десяткам страниц на компонент и чтобы работало на десятках устройств с разным отображением)
Тем не менее, бэковые микросервисы не представляют собой единую систему, каждый из них можно переписывать и поддерживать лишь протокол общения. Во фронтенде же все компоненты находятся в едином дом-дереве с общим полем раскладки и пространством стилей, режимом отображения. Мне доводилось создавать системы на изолированных iframe с разных доменов — вот это по факту действительно «микрофронтенды» — но для поддержания общей темы, локализации, размеров, формата, полифиллов и тп пришлось сделать очень толстый АПИ на postMessage для синхронизации. И в то время я как раз и понял, что независимые части в цельной системе — это боль, поэтому сторонился подходов композиции фронта из «как бы независимых» частей, потому что это самообман.
На бэке стейтфул независимые сервисы дают массу преимуществ до того момента, как возникает потребность формировать композитные структуры — тогда добавляются дополнительные слои кеширования, промежуточные сборные базы подготовленных данных и вся обвязка с синхронизацией и т.п., так как делать каждый раз по десятку запросов в разные микросервисы становится чрезвычайно накладно, как бы протоколы общения и внутренняя производительность ни совершенствовались. Но это все решается грамотными архитектурами, сохраняя преимущества в виде независимого цикла развития микросервисов и четкой зоны ответственности. В общем, проблемы есть, но их поменьше, чем если делать подобное на фронте
Почему же не сплю? При средней продуктивности 2к строк качественного кода в неделю сотня хуков набирается за рабочий месяц, при использовании хуков в виде замены классовых методов. Ну и побольше, чем год уже хукам, если статью по ним писал в апреле 2019 уже по релизной версии Реакта)
Про мое умение компонировать сейчас не очень хочется говорить, если буду писать статью, то представлю полдесятка концептов композиции функций, но сделаю вывод, что по удобству разработки все они не дотягивают до классовых методов. Пока что тут нечего обсуждать.
Посмотрел видео, про контекст там не говорится, там про передачу стейта несколькими путями, кроме контекста и инджекта, а я использую как раз последние в продуктовой разработке.
2.3 мегабайта — это очевидно за гранью разумного, в плане объема кода при небольшом функционале ванилла или Svelte выигрывают значительно. Но по мере усложнения приложения эта разница становится обратной, так что лендинги и простые сайты я продолжаю делать на нативном js, но для корпоративной разработки — только React+MobX.
Да, очень кратко аргументы приводил в этом треде, но без примеров реального кода и прямого сравнения альтернативных реализаций сложных компонентов выглядит недостаточно обоснованно. В целом можно разбить аргументы на семантические (синтаксис, основанный на соглашениях), анти-best-practice (смешение синхронного и асинхронного кода, сайд-эффекты в колбэках, раздувание чистых функций и смешение ответственности), сложностей в композиции (т.к. нет доступа к общим props и context, приходится передавать их в каждую функцию напрямую, что приводит к запутанному клубку), деградации перфоманса (т.к. приходится либо обкладывать многие части useCallback / useMemo, либо логика будет пересоздаваться на каждый ререндер и не будет работать сравнение по ссылкам).
Есть и плюсы, конечно, у этих хуков, но реализовав несколько проектов с очень сложными компонентами (с композицией десятков и сотен хуков), первоначальное желание с ними работать пропало. Я не готов обсуждать детально в комментариях эти аргументы сейчас, так как сам не проводил тщательное параллельное сравнение, это просто навскидку.
«Вам не нужно состояние какой-то отдельной формы на глобальном уровне» — в основном как раз нужно.
Зачем? Конечно такие кейсы возможны, но было бы интересно узнать при каких типовых сценариях это может пригодиться.
3) Для добавления валидаций — если бэк вернул ошибку, что такой email занят, то нужно записать это в список валидаций данного поля
4) При изменении значений в форме можно влиять на другие элементы на странице не событиями form.onChange(values => if (values.fieldValue === 'darkmode') changeWebsiteStyle('darkmode')), так как подобную модель поддерживать сложно и она приводит к «клубку зависимостей», а на глобальном уровне с распространением изменений из стора.
В целом мы скорее зашли в тупик, я говорю — что глобальный стейт позволяет избежать иерархий, лишних вложенностей и импортов, а вы в ответ — почему бы не использовать иерархии и оборачивать только те компоненты, в которых эти стейты используются. Я не согласен с тем, что это проще, и приводил массу сценариев, но, похоже, вас не переубедить, поэтому предлагаю остановить дискуссию и пусть читающие ветку комментариев сами оценивают силу аргументов каждой из сторон.
Компонентами можно назвать в том числе и обычные html-теги с атрибутами, например <div class=«myclass» onclick=«alert(1)» /> — тут и определенная семантичность в виде предустановленной блочной модели отображения, и стилизация, и методы, и состояние, которое в данном случае вынесено в стилевой класс (например, display: none или положение на странице). Реакт-компоненты выполняют роль агрегатора нескольких таких компонентов с возможностью управлять более крупными сущностями, которые становятся при этом более семантичными + интегрированными в js.
Но насколько изолированность — это благо? Вот несколько дивов, их стейты находятся в стилевых классах и задают, к примеру, абсолютное положение. Задача — сделать их взаимную раскладку в виде мозаики, причем при наведении на один из элементов он увеличивается и сдвигает остальные. Но так как они независимы и не знают ничего о друг о друге, то это сделать можно только независимыми математическими расчетами в стилях, при этом вбивая отступы в пикселях в качестве констант. А если бы у них был глобальный стейт типа `elements: [{ left, right, width, height }, ...]`, то это можно было бы сделать динамической формулой и гибко задавать условия взаимного расположения и интеракций.
Точно так же это экстраполируется на более крупные компоненты, и примеры реальных задач, в которых нужно знать о внутренних характеристиках и иметь возможность влиять на них, я привел в комментарии выше. Можно даже по примеру из прошлого абзаца — это будет например список нотификаций или галерея, и по клику на кнопку в другой части страницы нужно закрывать все нотификации или вызывать новые, изменять размеры и положение (сортировку) изображений в галерее. Чтобы наглядней — эта управляющая кнопка далекооо, в компонент Notifications / Gallery ее не поместить.
Так что «чем более независимыми являются компоненты — тем, как правило, лучше» — однозначно нет, не лучше. В глобальном стейте вам не помешает дополнительный параметр, а в локальном будет неудобен при возникновении потребности обратиться к нему — почему бы сразу не использовать удобное решение? Вы в целом не можете сделать совсем изолированные компоненты в интерфейсах, у них так или иначе будет общая раскладка, общие слои расположения, взаимное влияние — это совсем не бэк на микросервисах.
«Вам не нужно состояние какой-то отдельной формы на глобальном уровне» — в основном как раз нужно.
«Зачем синхронизировать то что не должно быть синхронизированным?» — что значит синхронизировать? Подразумевается, что я предлагаю хранить состояние одновременно в локальном и глобальном стейте? Нет, только в одном, никакой синхронизации не требуется.
«Мы точно знаем к чему то или иное состояние относится» — нет, если провайдер оборачивает 10 компонентов, то использоваться может лишь в двух, или вообще в глубоко вложенных. Точно знать в данном случае можно только посмотрев реализацию конкретного компонента, проанализировав, какие контексты он импортит. Иерархичность тут абсолютно бесполезна и приводит лишь к путанице.
«Применять какой-то подход к центральному состоянию, и каким будет этот подход? Скорее всего это будет что-то редаксо-подобное» — если это касается меня, это будет что-то реактивное и мутабельное.
Про мышление высокоуровневыми стейтами — я же предлагаю то же самое, только не разбивая на сотни провайдеров, а сделав единую точку входа в стор из сотни подсторов. И думать о каждом из них можно как об изолированном блоке, просто не надо думать об иерархии и делать массу импортов.
По поводу проблем с оптимизацией — я говорил в комментарии об этом же, что контексты не оптимизированы для использования. И то, что внедрив дополнительный уровень в виде иерархий, можно частично улучшить ситуацию — не вариант. Поэтому и сказал, что даже если мы сделаем стейт глобальным и сократим количество импортов и бесполезную иерархию, эта система стейт-менеджемента все равно не годится для серьезных проектов.
Здравствуйте, а можно чуть подробнее? В редаксе приняты многочисленные одноуровневые сторы, а что вы подразумеваете под мультисторами мобх и жизненным циклом? Если это действительно выглядит проблемой, подумаю над решением
У реакт-хуков, раз уж статья затронула эту тему, масса недостатков. Я вернулся на классы и в массе случаев это удобней. Стоит ли сейчас писать статью на пике хайпа, чтобы триггернуть фазу «избавление от иллюзий»?
«Держите состояние как можно ближе к месту к которому оно относиться» — я придерживаюсь другой парадигмы, в разработке приложений, в которых участвовал, локальные стейты приводили к дубляжу и сложному контролю. Идея о том, что можно хранить информацию о состоянии приложения в едином стейте, и каждый компонент может в любое время получить информацию о любой части и взять под контроль ее отображение, стала значительным апгрейдом в плане проектирования удобных систем.
Нужно ли закрыть модалку по сабмиту формы, добавить валидации к полям, исходя из значений в форме перерисовать лэйаут, в едином месте настраивать аналитику (вместо интеграции в каждый компонент), получить более низкоуровневый доступ к верстке компонента, сделать слепок состояния и восстановить для различных целей — глобальное состояние удобнее в огромном количестве реальных задач. А локальное, хотя кажется и не очень удобным выносить tooltipIsShown наружу из компонента Tooltip, но в реальности это непередаваемо удобней, когда можно закрывать все тултипы или показывать нужный по независимой от ховера мышки логике, вместо `querySelectorAll('[data-role=«tooltip»]').click()` — и тут еще нужно проверить состояние, чтобы не закрылись/открылись лишние. А такого кода параллельно с Реактом видел неприлично много.
В общем, пришел к убеждению, что любители локальных стейтов не сталкивались с более-менее сложными интерфейсами, так как этот подход проигрывает по всем фронтам. Цельный интерфейс — как живой организм, а дробленый на «черные коробки» — как непредсказуемая система из броуновских движений. Для бэка подойдет, так как там слишком много логики бывает в сервисах, а задач на составление «цельной картины» из состояния микросервисов может никогда и не появиться, а вот для фронтенда это — антипаттерн, так как интерфейс в каждый момент времени — цельная система, а синхронизация состояний — ключевой момент.
По поводу контекстов — думаю, Кент глаза никому не открыл, при изучении Реакта все так или иначе знакомятся с этой основополагающей концепцией) А вот мысль разбивать хранилище на разноуровневые и подключать каждое отдельно — глупость. Что мешает сделать один StateProvider, и внутри компонентов выбирать нужную его часть?
Импортов и кода меньше, возиться с уровнями и отсутствием проброшенного не нужно. А внутри стора можно как угодно скомпоновать логику и методы, удобно разбив по нескольким файлам.
Только вот от стора требуется реактивность, а от коннектора к компоненту — имплементация shouldComponentUpdate, чтобы компонент обновлялся только при изменении нужных ему данных. В случае с чистыми контекстами ни реакций на изменение, ни оптимизированных ререндеров ждать не приходится, да и обкладываться вот таким
и вызывать по функции вроде setCount для изменения одного значения вместо батчей и мутаций подойдет только для странички с каунтером, но не для более-менее сложного сайта.
Спасибо за перевод, понравилось в статье, что автор топит за отказ от Redux — указанный подход через контексты хоть и не удобен, но все-таки более прогрессивен, на мой взгляд.
Знают, но большинство новых проектов стартуют на Redux, судя по тем собеседованиям, на которые хожу. Почему?
В свое время это был хайп, который здравомыслящие люди обходили стороной ввиду больших сложностей применения на практике, но пора бы уже подобным концептам уйти на покой и быть признанными нежизнеспособными. Однако разработчики продолжают по каким-то причинам мусолить эту тему, а Абрамов вместо того, чтобы признать, что создал стремную систему, еще и в реакт-хуки запихивает имплементацию. Как тут без эмоций?
Redux — сложная и перегруженная лишним система, вы лишь немного ее упростили. Пайпы, диспатчи, билдеры, возвращение целого объекта стора в каждом экшене в иммутабельном формате… Это не просто и смысл создания подобного очень туманен. Без проблем готов обстоятельно сравнить с observable-стором, в котором все решения будут выглядеть намного чище и проще.
Точно, тоже хотел про это упомянуть) Авторизация через модальные окна работает не везде — в мобильных устройствах и при включенных блокировщиках рекламы либо особых настройках безопасности браузера они могут просто не открываться, соответственно нужно создавать систему «если не сработала модалка — применить схему редиректа». Во втором случае пользователя перебрасывает на сторонний сайт и обратно, это затраты его времени и трафика, а с точки зрения основного сайта — дополнительные запросы и нагрузка на сервер + лишние срабатывания событий аналитики (ведь обратный редирект считается новым заходом), и аналитикам приходится создавать дополнительные схемы для фильтрации подобного поведения.
Если же работает через модалку, то она не стилизуется под сайт и выглядит некрасивым инородным элементом.
Считается, что все эти ux-неудобства окупаются тем, что пользователю не требуется создавать новый логин-пароль и запоминать его, так как потерпеть +-10 секунд редиректы или некрасивые модалки — это проще, чем заставлять мозг креативить для создания новой пары ключей доступа и помещать это дело в хранилище (я, например, пользуюсь LastPass, поэтому это не проблема, но многие хранят в текстовых файлах или приватных сообщениях в мессенджерах, в худшем случае — только в памяти). А если сайт требует «безопасный пароль» с разным регистром, цифрами, спецсимволами и длинный, то тут очевидно справятся только самые стойкие и очень заинтересованные в использовании данного сервиса.
В общем, с точки зрения ux оба решения далеки от идеала. А технологии биометрической идентификации пока недостаточно внедрены в устройства, работающие с веб-сайтами, так что приходится выбирать из того, что есть.
А для чего это знать? Юзер авторизуется в стороннем сервисе, на бэк приходит токен, валидируешь его в стороннем сервисе и получаешь минимальный набор данных — email, id, срок действия токена. Формат поступивших данных, конечно, лучше провалидировать, но никакой опасности эта схема не представляет.
Очевидные недостатки лежат в другой плоскости — это доступность сервиса (и время отклика) и открытость информации. Так, сервис авторизации узнает, в какое время определенный человек (его регион, характеристики системы и часто имя, сохраненное во внутренней базе этого сервиса, телефон, email и т.п. — если брать например авторизацию через сервисы Google) заходил на определенный сайт и примерное время его пребывания (по количеству запросов на верификацию токена от нашего бэка). То есть подобные системы — часть «ока большого брата», которое в свои bigdata-хранилища собирает информацию о людях и их предпочтениях. А разработчики с удовольствием предоставляют эту информацию, так как реализовывать шифрование паролей и систему против взлома довольно морочно для проектов не-энтерпрайз уровня. Энтерпрайзные же приложения в большинстве случаев все равно используют один из сервисов аналитики (или несколько через GTM), так что в любом случае предоставляют эти данные, только через другой канал.
На самом деле это довольно резонный консерн, к которому серьезно относятся в приложениях, требующих высокого уровня конфиденциальности, но в остальных случаях никто особо не заморачивается.
Спасибо за перевод, не хватает только примеров кода. А в целом подход стар и в свое время широко раскручивался в виде отказа от собственной авторизации по логину-паролю, отдавая этот функционал стороннему сервису, который после авторизации отдаст токен. Маппинг в собственной базе при этом происходит либо по содержащемуся в токене id, либо по email.
На практике используется широко и недостатков не так уж и много — только затраты времени на запросы в сторонний сервис для валидации токена и возня с апи различных сервисов и их схемами работы. Из библиотек могу посоветовать www.passportjs.org, но единую схему работы для нескольких провайдеров он не предоставляет, код будет выглядеть довольно перегруженным.
Спасибо за идею, сейчас подумаю. Ограничения на значения можно сделать простым перечислением констант `«POST » | «GET»`, как в статье, а проверку регулярками (которые в том числе могут проверять длину строки) либо функциями я реализую таким образом:
В данном случае можно писать либо уникальные проверки, либо синхронизировать с другими валидаторами — например, полей формы. В общем, достаточно удобно.
JSON-схему писать довольно муторно, количество условий сильно разрастается, и вместо гибких функций, как в примере выше, приходится создавать множество параметров — а если требуется композиция с другими валидациями (formValidators), то соединять разные JSON-схемы приходится строковыми ссылками, что автоматически переводит проект в разряд неявного и сложно поддерживаемого.
Также перевод несовместимых сущностей друг в друга в любом случае не получится корректным. Если написать валидаторы, состоящие из регулярок и проверочных функций с композицией с другими валидаторами — то типы из этого не сгенерировать, разве что только брать ReturnValue — но он по идее всегда будет Boolean. Так что не вижу варианта использовать подобное на практике, к сожалению.
Попробуйте. Видимо, вам попадались проекты на Redux — я тоже недавно ушел из проекта, в котором на каждое вбивание символа в форму весь сайт с тысячами компонентов (виртуальная библиотека) ререндерился 17 раз, и это пару лет работало в продакшене, с неплохой посещаемостью. То есть с одной стороны перфоманс переоценен — если продукт действительно полезный и решает проблемы людей, то хоть вот так вот может работать. А с другой стороны — самый лучший проект — тот, который написан грамотно и заботливо, а не на хайповых технологиях, и 10-15-летние старые проекты на vanilla и сейчас дают фору кое-как слепленным из опенсорса монстрам (к слову, бандл позапрошлого проекта в несжатом виде был 97 мегабайт, и собирался соответствующее время, тоже реакт-редакс).
В этом смысле олдскульные практики экономить каждый килобайт и делать css-only-интерактив (модалки, слайдеры и тп) вместо js, да и еще nojs-версии, вызывают добрую ностальгию. Но подойдите к снаряду еще пару раз — я привел реальные крупные примеры, но каждый из них удалось сокращать раза в 3 и делать удобным в разработке и быстрым как раз благодаря современным практикам реактивности.
Присоединяюсь к комментирующим — пост полон непродуманных утверждений.
— «тотально доминирует реактивность» — совсем не тотально, только в наиболее продвинутых с технической точки зрения проектах.
— «проблем вроде централизованного хранилища данных» — проблемой является по большей части как раз распределенное хранилище, разбросанное по разным компонентам и базам. Централизованное же предоставляет удобную возможность обращаться за актуальными данными из любого места, делать «слепки состояний», точно знать какими данными оперирует приложение и удобно рефакторить.
— «почему собственно данные должны храниться в объектах JS, а не напрямую в узлах DOM?» — концепт переноса операций с данными и хранилищ в JS — молодой, до этого состояние хранили как раз в дата-атрибутах и была масса фреймворков с подобной схемой работы. Однако медленность, необходимость постоянной сериализации-десериализации и работа по неудобному DOM-апи (вместо js-методов) неизбежно привели к выводу, что разрабатывая на JS, нужно там же и хранить данные, а в браузерное представление трансформировать скрытым реактивным механизмом как сайд-эффект. Присопособленность терпеть недостатки выдает в вас олдскул, так как раньше действительно технологии не позволяли подобного)
— «подобная реактивность вообще не часто востребована» — даже не могу найти примеров, где не востребовано подобное удобство. На работу можно доехать и на «запорожце» (вручную работая с DOM), но других людей (команду) возить стыдно.
— «функциональщина + виртуальный DOM — вещи не бесплатные» — не знаю что значит «функциональщина», но сравнить пару объектов явно дешевле работы с DOM.
— «компоненты, управляемые разными реактивными библиотеками» — если у вас есть такое в проекте, выбрасывайте, а не ищите способы скрестить ужа с ежом.
Примеры кода даже комментировать не хочется, в 2005 мне бы понравился — прямо как в учебниках, createElement, prepend-append, event-driven модель передачи событий, верстка в строках… Для первого проекта — самое то, но не верю, что серьезно топите за это все.
В MobX тоже можно комбинировать «подсторы»:
То есть по способу компоновки сторов очень похоже. Вы, наверное, имеете в виду ситуацию, когда Store1 должен обратиться к Store2? Это решается несложно. А в редаксе другая схема работы — в нем сторы вообще не могут общаться, они просто содержат данные, а экшены могут обращаться с помощью глобальных констант к любому стору. Можно сделать такую же схему и на MobX, он никак не ограничивает возможности.
Вообще я как раз про кастомные хуки говорил) Бывает длиннющая цепочка из полусотни, кроме шуток, и «плоско» — это только на верхнем уровне, то есть в функции рендера, в то время как если распутать весь клубок, получится множество взаимосвязей и изменений одних и тех же параметров.
На бэке стейтфул независимые сервисы дают массу преимуществ до того момента, как возникает потребность формировать композитные структуры — тогда добавляются дополнительные слои кеширования, промежуточные сборные базы подготовленных данных и вся обвязка с синхронизацией и т.п., так как делать каждый раз по десятку запросов в разные микросервисы становится чрезвычайно накладно, как бы протоколы общения и внутренняя производительность ни совершенствовались. Но это все решается грамотными архитектурами, сохраняя преимущества в виде независимого цикла развития микросервисов и четкой зоны ответственности. В общем, проблемы есть, но их поменьше, чем если делать подобное на фронте
Про мое умение компонировать сейчас не очень хочется говорить, если буду писать статью, то представлю полдесятка концептов композиции функций, но сделаю вывод, что по удобству разработки все они не дотягивают до классовых методов. Пока что тут нечего обсуждать.
Посмотрел видео, про контекст там не говорится, там про передачу стейта несколькими путями, кроме контекста и инджекта, а я использую как раз последние в продуктовой разработке.
Есть и плюсы, конечно, у этих хуков, но реализовав несколько проектов с очень сложными компонентами (с композицией десятков и сотен хуков), первоначальное желание с ними работать пропало. Я не готов обсуждать детально в комментариях эти аргументы сейчас, так как сам не проводил тщательное параллельное сравнение, это просто навскидку.
3) Для добавления валидаций — если бэк вернул ошибку, что такой email занят, то нужно записать это в список валидаций данного поля
4) При изменении значений в форме можно влиять на другие элементы на странице не событиями
form.onChange(values => if (values.fieldValue === 'darkmode') changeWebsiteStyle('darkmode'))
, так как подобную модель поддерживать сложно и она приводит к «клубку зависимостей», а на глобальном уровне с распространением изменений из стора.В целом мы скорее зашли в тупик, я говорю — что глобальный стейт позволяет избежать иерархий, лишних вложенностей и импортов, а вы в ответ — почему бы не использовать иерархии и оборачивать только те компоненты, в которых эти стейты используются. Я не согласен с тем, что это проще, и приводил массу сценариев, но, похоже, вас не переубедить, поэтому предлагаю остановить дискуссию и пусть читающие ветку комментариев сами оценивают силу аргументов каждой из сторон.
Но насколько изолированность — это благо? Вот несколько дивов, их стейты находятся в стилевых классах и задают, к примеру, абсолютное положение. Задача — сделать их взаимную раскладку в виде мозаики, причем при наведении на один из элементов он увеличивается и сдвигает остальные. Но так как они независимы и не знают ничего о друг о друге, то это сделать можно только независимыми математическими расчетами в стилях, при этом вбивая отступы в пикселях в качестве констант. А если бы у них был глобальный стейт типа `elements: [{ left, right, width, height }, ...]`, то это можно было бы сделать динамической формулой и гибко задавать условия взаимного расположения и интеракций.
Точно так же это экстраполируется на более крупные компоненты, и примеры реальных задач, в которых нужно знать о внутренних характеристиках и иметь возможность влиять на них, я привел в комментарии выше. Можно даже по примеру из прошлого абзаца — это будет например список нотификаций или галерея, и по клику на кнопку в другой части страницы нужно закрывать все нотификации или вызывать новые, изменять размеры и положение (сортировку) изображений в галерее. Чтобы наглядней — эта управляющая кнопка далекооо, в компонент Notifications / Gallery ее не поместить.
Так что «чем более независимыми являются компоненты — тем, как правило, лучше» — однозначно нет, не лучше. В глобальном стейте вам не помешает дополнительный параметр, а в локальном будет неудобен при возникновении потребности обратиться к нему — почему бы сразу не использовать удобное решение? Вы в целом не можете сделать совсем изолированные компоненты в интерфейсах, у них так или иначе будет общая раскладка, общие слои расположения, взаимное влияние — это совсем не бэк на микросервисах.
«Вам не нужно состояние какой-то отдельной формы на глобальном уровне» — в основном как раз нужно.
«Зачем синхронизировать то что не должно быть синхронизированным?» — что значит синхронизировать? Подразумевается, что я предлагаю хранить состояние одновременно в локальном и глобальном стейте? Нет, только в одном, никакой синхронизации не требуется.
«Мы точно знаем к чему то или иное состояние относится» — нет, если провайдер оборачивает 10 компонентов, то использоваться может лишь в двух, или вообще в глубоко вложенных. Точно знать в данном случае можно только посмотрев реализацию конкретного компонента, проанализировав, какие контексты он импортит. Иерархичность тут абсолютно бесполезна и приводит лишь к путанице.
«Применять какой-то подход к центральному состоянию, и каким будет этот подход? Скорее всего это будет что-то редаксо-подобное» — если это касается меня, это будет что-то реактивное и мутабельное.
Про мышление высокоуровневыми стейтами — я же предлагаю то же самое, только не разбивая на сотни провайдеров, а сделав единую точку входа в стор из сотни подсторов. И думать о каждом из них можно как об изолированном блоке, просто не надо думать об иерархии и делать массу импортов.
По поводу проблем с оптимизацией — я говорил в комментарии об этом же, что контексты не оптимизированы для использования. И то, что внедрив дополнительный уровень в виде иерархий, можно частично улучшить ситуацию — не вариант. Поэтому и сказал, что даже если мы сделаем стейт глобальным и сократим количество импортов и бесполезную иерархию, эта система стейт-менеджемента все равно не годится для серьезных проектов.
Нужно ли закрыть модалку по сабмиту формы, добавить валидации к полям, исходя из значений в форме перерисовать лэйаут, в едином месте настраивать аналитику (вместо интеграции в каждый компонент), получить более низкоуровневый доступ к верстке компонента, сделать слепок состояния и восстановить для различных целей — глобальное состояние удобнее в огромном количестве реальных задач. А локальное, хотя кажется и не очень удобным выносить tooltipIsShown наружу из компонента Tooltip, но в реальности это непередаваемо удобней, когда можно закрывать все тултипы или показывать нужный по независимой от ховера мышки логике, вместо `querySelectorAll('[data-role=«tooltip»]').click()` — и тут еще нужно проверить состояние, чтобы не закрылись/открылись лишние. А такого кода параллельно с Реактом видел неприлично много.
В общем, пришел к убеждению, что любители локальных стейтов не сталкивались с более-менее сложными интерфейсами, так как этот подход проигрывает по всем фронтам. Цельный интерфейс — как живой организм, а дробленый на «черные коробки» — как непредсказуемая система из броуновских движений. Для бэка подойдет, так как там слишком много логики бывает в сервисах, а задач на составление «цельной картины» из состояния микросервисов может никогда и не появиться, а вот для фронтенда это — антипаттерн, так как интерфейс в каждый момент времени — цельная система, а синхронизация состояний — ключевой момент.
По поводу контекстов — думаю, Кент глаза никому не открыл, при изучении Реакта все так или иначе знакомятся с этой основополагающей концепцией) А вот мысль разбивать хранилище на разноуровневые и подключать каждое отдельно — глупость. Что мешает сделать один StateProvider, и внутри компонентов выбирать нужную его часть?
Импортов и кода меньше, возиться с уровнями и отсутствием проброшенного не нужно. А внутри стора можно как угодно скомпоновать логику и методы, удобно разбив по нескольким файлам.
Только вот от стора требуется реактивность, а от коннектора к компоненту — имплементация shouldComponentUpdate, чтобы компонент обновлялся только при изменении нужных ему данных. В случае с чистыми контекстами ни реакций на изменение, ни оптимизированных ререндеров ждать не приходится, да и обкладываться вот таким
и вызывать по функции вроде setCount для изменения одного значения вместо батчей и мутаций подойдет только для странички с каунтером, но не для более-менее сложного сайта.
Спасибо за перевод, понравилось в статье, что автор топит за отказ от Redux — указанный подход через контексты хоть и не удобен, но все-таки более прогрессивен, на мой взгляд.
В свое время это был хайп, который здравомыслящие люди обходили стороной ввиду больших сложностей применения на практике, но пора бы уже подобным концептам уйти на покой и быть признанными нежизнеспособными. Однако разработчики продолжают по каким-то причинам мусолить эту тему, а Абрамов вместо того, чтобы признать, что создал стремную систему, еще и в реакт-хуки запихивает имплементацию. Как тут без эмоций?
Если же работает через модалку, то она не стилизуется под сайт и выглядит некрасивым инородным элементом.
Считается, что все эти ux-неудобства окупаются тем, что пользователю не требуется создавать новый логин-пароль и запоминать его, так как потерпеть +-10 секунд редиректы или некрасивые модалки — это проще, чем заставлять мозг креативить для создания новой пары ключей доступа и помещать это дело в хранилище (я, например, пользуюсь LastPass, поэтому это не проблема, но многие хранят в текстовых файлах или приватных сообщениях в мессенджерах, в худшем случае — только в памяти). А если сайт требует «безопасный пароль» с разным регистром, цифрами, спецсимволами и длинный, то тут очевидно справятся только самые стойкие и очень заинтересованные в использовании данного сервиса.
В общем, с точки зрения ux оба решения далеки от идеала. А технологии биометрической идентификации пока недостаточно внедрены в устройства, работающие с веб-сайтами, так что приходится выбирать из того, что есть.
Очевидные недостатки лежат в другой плоскости — это доступность сервиса (и время отклика) и открытость информации. Так, сервис авторизации узнает, в какое время определенный человек (его регион, характеристики системы и часто имя, сохраненное во внутренней базе этого сервиса, телефон, email и т.п. — если брать например авторизацию через сервисы Google) заходил на определенный сайт и примерное время его пребывания (по количеству запросов на верификацию токена от нашего бэка). То есть подобные системы — часть «ока большого брата», которое в свои bigdata-хранилища собирает информацию о людях и их предпочтениях. А разработчики с удовольствием предоставляют эту информацию, так как реализовывать шифрование паролей и систему против взлома довольно морочно для проектов не-энтерпрайз уровня. Энтерпрайзные же приложения в большинстве случаев все равно используют один из сервисов аналитики (или несколько через GTM), так что в любом случае предоставляют эти данные, только через другой канал.
На самом деле это довольно резонный консерн, к которому серьезно относятся в приложениях, требующих высокого уровня конфиденциальности, но в остальных случаях никто особо не заморачивается.
На практике используется широко и недостатков не так уж и много — только затраты времени на запросы в сторонний сервис для валидации токена и возня с апи различных сервисов и их схемами работы. Из библиотек могу посоветовать www.passportjs.org, но единую схему работы для нескольких провайдеров он не предоставляет, код будет выглядеть довольно перегруженным.
В данном случае можно писать либо уникальные проверки, либо синхронизировать с другими валидаторами — например, полей формы. В общем, достаточно удобно.
JSON-схему писать довольно муторно, количество условий сильно разрастается, и вместо гибких функций, как в примере выше, приходится создавать множество параметров — а если требуется композиция с другими валидациями (formValidators), то соединять разные JSON-схемы приходится строковыми ссылками, что автоматически переводит проект в разряд неявного и сложно поддерживаемого.
Также перевод несовместимых сущностей друг в друга в любом случае не получится корректным. Если написать валидаторы, состоящие из регулярок и проверочных функций с композицией с другими валидаторами — то типы из этого не сгенерировать, разве что только брать ReturnValue — но он по идее всегда будет Boolean. Так что не вижу варианта использовать подобное на практике, к сожалению.
В этом смысле олдскульные практики экономить каждый килобайт и делать css-only-интерактив (модалки, слайдеры и тп) вместо js, да и еще nojs-версии, вызывают добрую ностальгию. Но подойдите к снаряду еще пару раз — я привел реальные крупные примеры, но каждый из них удалось сокращать раза в 3 и делать удобным в разработке и быстрым как раз благодаря современным практикам реактивности.
— «тотально доминирует реактивность» — совсем не тотально, только в наиболее продвинутых с технической точки зрения проектах.
— «проблем вроде централизованного хранилища данных» — проблемой является по большей части как раз распределенное хранилище, разбросанное по разным компонентам и базам. Централизованное же предоставляет удобную возможность обращаться за актуальными данными из любого места, делать «слепки состояний», точно знать какими данными оперирует приложение и удобно рефакторить.
— «почему собственно данные должны храниться в объектах JS, а не напрямую в узлах DOM?» — концепт переноса операций с данными и хранилищ в JS — молодой, до этого состояние хранили как раз в дата-атрибутах и была масса фреймворков с подобной схемой работы. Однако медленность, необходимость постоянной сериализации-десериализации и работа по неудобному DOM-апи (вместо js-методов) неизбежно привели к выводу, что разрабатывая на JS, нужно там же и хранить данные, а в браузерное представление трансформировать скрытым реактивным механизмом как сайд-эффект. Присопособленность терпеть недостатки выдает в вас олдскул, так как раньше действительно технологии не позволяли подобного)
— «подобная реактивность вообще не часто востребована» — даже не могу найти примеров, где не востребовано подобное удобство. На работу можно доехать и на «запорожце» (вручную работая с DOM), но других людей (команду) возить стыдно.
— «функциональщина + виртуальный DOM — вещи не бесплатные» — не знаю что значит «функциональщина», но сравнить пару объектов явно дешевле работы с DOM.
— «компоненты, управляемые разными реактивными библиотеками» — если у вас есть такое в проекте, выбрасывайте, а не ищите способы скрестить ужа с ежом.
Примеры кода даже комментировать не хочется, в 2005 мне бы понравился — прямо как в учебниках, createElement, prepend-append, event-driven модель передачи событий, верстка в строках… Для первого проекта — самое то, но не верю, что серьезно топите за это все.