Как стать автором
Обновить

Комментарии 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.

Либо просто конфигурировать без подражания html.

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.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 будет ре-маунтить его на каждый рендер.

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

так-то да, использовать рендер-функцию, как и написано в документации

так что и возникает вопрос "почему это проблема Реакт-Роутера?", ведь это дефолтное повидение в Реакте

Не нашел в документации к 6-й версии чтобы можно было рендер функцию передавать. Можете показать место где про это написано?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории