Комментарии 24
Не сочтите меня ярым фанатиком react-router, но я почти со всем не согласен.
Получаем такие этапы создания роутинга:
Создание единого объекта с конфигурацией роутов
Рекурсивный проход этого объекта, чтобы сгенерировать в jsx набор реакт компонентов <Route/>
У меня на проекте был товарищ, который применял такой подход ко всему. он делал конфиги, а из них делал jsx. Только вот за этой чехардой он забывал, что jsx это сам по себе конфиг, просто не в json, а в jsx.
То есть считаю {routeConfig.map((route) => <Route {...route}/>)}
антипаттерном, CMV
Абсолютные пути
Но я бы предпочел, чтобы это разруливала библиотека роутинга. Мы в конфиге передаем какой модуль внутри какого находится. Библиотека роутинга может вычислить из относительного пути абсолютный.
Не понимаю причем тут библиотека роутинга. Библиотека занимается роутингом, а вы требуете у неё знать о существовании модулей, о том что они друг в друге и прочее.
Вы точно так же можете наворотить своих конфигов, но только для ссылок.
const bookmodule = {
path: '/book',
}
const dashboardmodule = {
path: '/dashboard',
modules: [bookmodule]
}
const shopmodule= {
path: '/shop',
//modules: [bookmodule]
}
const config = makeMyOwnConfigForMyApp([dashboardmodule, shopmodule])
config.book.getLink() === '/dashboard/book/'
Но у <Header/> меняется только один пропс title — полностью его пересоздавать — это явно излишество.
ну дык this is the way. Не, правда, ввинять библиотеке роутинга что она пересоздает компонент потому что вы запихнули его в роут это жестко. Если проблема только в оптимизации, то тут проще Header оптимизировать. Вообще пункт был самый мощный, пока я не пошел изучать v6 и не нашел там в базовом примере (https://reactrouter.com/docs/en/v6/examples/basic):
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="*" element={<NoMatch />} />
</Route>
И тут можно всегда добавить: <Route path="/noheader" element={<NoHeader />} />
и это логично, потому что у вас будет
не "каждый компонент обязан знать о хедере и ещё и вставлять его сам",
а вменяемое дерево лейаутов.
</Routes>
Если вдруг вы решили (как и я) отделить бизнес логику от UI, то, думаю, будет логично, чтобы UI (который в JSX) только дергал событие “нажата кнопка X”, а уже сам переход делала бизнес логика.
??? Вообще не понял какое это отношение имеет к разделению БЛ и UI. Переход это БЛ? А почему тогда хендлер знает о юайном событии? Почему он написан сверху этого jsx, "во вью"? Как будто для красного словца ввернули просто.
Если уж "разделять", то как и в п.2:
юай: <Link to={config.usermodule.getLink(userId)}/>
сложная бизнес логика: megaSuperCallback = () => {
…
history.pushRoute navigate(config.usermodule.getLink(userId))
}
Я понимаю если бы статья была из одной строчки: "меня задрало как они меняют АПИ от версии к версии" — тут нечего сказать, получи плюс и крути педали дальше, но вот ваш рассказ навел на мысль, что вы вместо простого и понятного роутинга хотите чтобы у вас был монстр из конфигов на конфигах который прикрываясь благими паттернами на самом деле замешивал бы весь роутинг, лейаутинг, БЛ и все остальное в одну большую кашу.
что jsx это сам по себе конфиг
Касательно этой фразы - она не противоречит статье. Полностью с ней согласен и у меня даже была статья на эту тему.
Те конвертации конфига в jsx, с которыми я работал, были не мной написаны. И я как раз против конвертации конфига в JSX. Я за то чтобы либо конфиг либо jsx.
Но мысль в этой статье была про то что роуты не удобно конфигурировать JSX-ом и почему - я аргументировал.
jsx - это программа создающая конфиг (vdom), но не сам конфиг.
ну имеется ввиду, что мы либо можем придумывать свой конфиг, а потом из него генерить JSX, либо сразу описывать (конфигурировать) программу с помощью jsx.
Jsx это не программа а расширение языка js и да, человек выше прав. Конфигурировать роуты с помощью jsx удобно. Тот же конфиг, но более наглядно. А так абстракция на абстракции
Библиотека занимается роутингом, а вы требуете у неё знать о существовании модулей
Я не требую знать о существовании модулей. Я предоставляю библиотеке конфиг с деревом роутов (не важно, через jsx или через useRouter). Этого должно быть достаточно, чтобы она вычислила абсолютный путь к элементу этого дерева.
Вы точно так же можете наворотить своих конфигов, но только для ссылок.
Могу, но зачем мне писать два конфига и следить чтобы они были консистентными? Чем больше кода, тем дороже его поддерживать.
не "каждый компонент обязан знать о хедере
Видимо тут у нас с вами основное различие во взглядах. Я считаю что каждая страница должна полностью определять весь лейаут страницы. Все остальные решения не универсальны. Вызвать в каждой странице свой лейаут (<Layout1><Page1Content/></Layout1>) практически ничего не усложняет, но увеличивает гибкость до максимума. Мы на любой каприз заказчика можем поменять хеадер, футер или сайдбар, поменяв Layout1 на Layout2.
То что у них в примере есть общий лейаут для внутренних роутов - это не универсально. Всегда есть вероятность, что заказчик попросит сделать одну из внутренних страниц по другому лейоуту. И тогда начнутся костыли.
Поэтому ссылка на книгу будет выглядеть так:
Когда в моем проекте пришёл момент этот, я переписал роты на статичные и все остальное перенёс в query. Жить стало значительно легче
Самый несущественный. Лишние пересоздания кокомпонентове
Если у вас такая проблема, то наверняка вы неверно, особенно с шапкой, организовали роутинг. Как уже сказали выше, ту же шапку можно вынести из роута и всё, а все остальное можно проверить в одном месте, где роутинг конфигурируется, даже тот же location.
Хм, но если вам так хочется использовать функции вместо компонентов, то … почему бы не использовать их?
<Route path="/home" element={Home()}>
<Route path="/about" element={About()}>
Неужели роутер каким-то чудом запрещает подобное?
Спасибо, вариант рабочий, ничего не пересоздается. Но вы же не предлагаете одновременно создать 50 страниц и держать их в памяти?
Так вы же держите в памяти не 50 страниц, а их jsx-описания. Если побить каждую на крупные блоки, и не позволять контенту "проникать" наружу — эти 50 "страниц" не займут много места.
Опять-таки, вы можете воспользоваться своим же трюком с вспомогательным компонентом.
Если в оных компонентах есть хуки, то всё развалится - хуки станут условными и вообще поедут "наружу".
Да и не совсем понятно, насколько такой способ совместим с React.memo или mobx observer
Изо дня в день вижу как программисты жалуются на эту React библиотеку, но продолжают на ней работать. Ёжик плакал, кололся, но продолжал есть кактус.
Да, я осознаю, что в этом подходе перестанет работать встроенный функционал браузера "открыть в новом окне", это надо будет отдельно реализовывать, но у всего есть свои плюсы и минусы.
Боюсь что без <a href/>
ваше решение не имеет смысла даже обсуждать. Это настолько грубое наплевательское отношение к пользователям и вообще основам web-а, что не совсем понятно, зачем вы вообще выбрали тогда фронтенд. Рутинг в обход ссылок большое зло.
Самой большой проблемой react-router
мне показалось не всё вышеописанное, а то, что URL является дополнительным источником истины. И если в проекте есть другой источник истины, то попытки их "примирение" может быть очень болезненными, т.к. они могут обновляться не своевременно. К примеру вначале обновится router, а потом какой-нибудь внешний store. Или наоборот. И вот это промежуточное состояние может быть очень неконсистентным. Если же попробовать сдружить router с внешним store-ом, то всё равно остаётся проблема самого компонента <Route/>
и его matches
, которые уже являются более локальными. В общем это может быть очень проблемной штукой.
<Link/>
, кстати довольно странная штука. Он хотя и является ссылкой (иначе всю либу можно было бы сразу выкинуть на свалку), но переходы организует посредством патченного onClick
. Абстракции построенные поверх абстракций.
при всей нелюбви к реакт-роутеру, половина пунктов тут мимо
от того, что "крутые конфиги" - не задача реакт-роутера, как и попытка оптимизировать спички через JSX, до
"Лишние пересоздания компонентов." - тут совсем не проблема реакт-роутера. Если динаммически создавать компоненты без сохранения равенства ссылок, а потом делать React.createElement с ними, то само собой Reconciler будет ре-маунтить его на каждый рендер.
А вот то, что реакт-роутер пишут больные люди - это точно. Уже пару версий подряд не могут удержать API в консистентном виде (проходит 3-4 месяца, и не можешь понять - это я документацию найти не могу, или опять что-то поменялось). Выпускают кучу версий, которые сломаны и юзать их попросту нельзя (особенно на 4.+ Просто ждите, когда npm затянет что-то "совместимое" и все хуки просто не будут работать). Ну и самое интересное - какая-то общая мания с переименованиями (смотрю на тебя exact, чем ты не угодил - не знаю)
"Лишние пересоздания компонентов." - тут совсем не проблема реакт-роутера.
Ну да, это пробема Реакта, но вместо решения проблемы в корне, люди спорят о том, надо ли для каждого симптома делать свои костыли, или же каждый раз надо пафосно превозмогать.
то само собой Reconciler будет ре-маунтить его на каждый рендер.
Что значит "само собой"? Так говорите как будто нет выхода. Я привел пример как это можно было сделать, чтобы не было пересоздания. И для этого не надо писать что-то сверхъестественное. Достаточно передавать не элемент, а рендер функцию
Что мне не нравится в react-router