Show HN: React Routing in 120 lines (including comments)
github.com
github.com
This misses event.shiftKey. All keyboard modifiers disqualify client-side routing.
As for <Link> in its entirety, where you have to use a special component which becomes a link and adds a click handler to it, I think this is generally the wrong solution: it’s better to instead put a single click handler on the entire document to intercept clicks on links. That way, you can just use normal links everywhere and it just works, rather than having to remember to use a whole separate component every time which may or may not be able to pass through the required properties or attributes (e.g. this one only supports class and href). Simpler, smaller and cheaper.
The History API, as it stands, is global state. That makes it one of the very few places where I say it is better to use a global solution, rather than a precise one.
It can be problematic to access globals from functional programming contexts, but I don't really see how History would cause conflicts here. Do you have any examples?
If you are really worried about this then the proper way to solve it imo is to pass `history` in as a prop or Context instead of calling it from the global `window` directly. This follows functional paradigms and is more in line with React principles. This has some other advantages as well. For example, if you are using test frameworks like Jest, you can test the link component by mocking the History object being passed in.
Passing history in as a property doesn’t solve anything in the application: it’s still global state, and if multiple things try to use it (even if one is trying to use the hash and the other the path), they’ll interfere with each other and you’ll have a bad time.
Passing it as a property may help with mocking-based test frameworks (though I find mocking to normally be a bad technique—you get better results from unit tests and integration tests with real systems), though you may also be able to control the global variables to accomplish the same effect; yet as far as the application is concerned, it’s still global state.
So then: since anything working with the URL is unavoidably working with global state, I see no virtue in avoiding a global event handler.
In my experience, generally when people try to use global handlers, it ends up getting bloated as more and more teams add their quirks and features into those handlers. There are numerous ways to mitigate this but I've found that the easiest way to prevent these issues is to simply move the logic into components. And React context gets rid of the need for prop drilling and removes most of the boilerplate involved with passing data to deeply nested components.
Don't forget clicks other than "left". Middle click can open a new tab.
But there are definitely situations where it’s judicious to check event.button, and I missed altKey, too. Here’s the full function Fastmail uses:
const isClickModified = function (event) {
return (
!!event.button ||
event.altKey ||
event.ctrlKey ||
event.metaKey ||
event.shiftKey
);
};On a side note, I can't believe we still don't have a `visit` or `activate` event that works regardless of hardware, without having to exclude modifier keys.
addEventListener("click", function (event) {
const link = event.target.closest("a");
if (
!event.button &&
!event.altKey &&
!event.ctrlKey &&
!event.metaKey &&
!event.shiftKey &&
link &&
link.href.startsWith(location.origin + "/") &&
link.target !== "_blank"
) {
event.preventDefault();
navigate(link.href);
}
}
There are some points of nuance here where you may want to vary (e.g. the determination of eligible hrefs, and handling of href targets), but this is the bones of it. You can also choose to add more functionality to it. Fastmail’s webmail, for example, uses this basic design, but also handles mailto: links specially (taking you to compose a new email), whitelists path patterns (since the domain is used for more than just the webmail app), and one or two other things.(You might think that it should use .closest("a[href]") instead of .closest("a"), since a:not([href]) is not a link, but in that case, link.href === "", and so it fails the line 10 is-it-eligible-for-client-side-routing test. Note also that except for the “no href attribute” case, HTMLAnchorElement#href gives a full resolved URL, so <a href=""> will produce the document base URL.)
Fundamentally, there’s no such thing as an event handler that triggers only on links—you instead have to use a global event handler that starts by checking whether it’s being triggered on a link.
Normally it's backwards compatibility, but in this case it appears to be "everything".
These short libs are apples to oranges unless we have a test suite or set of acceptance criteria. Something like https://todomvc.com/ and https://eugenkiss.github.io/7guis/tasks helps keep reqs constant.
This is a nice demonstration of how routers can work for people that are curious, but I would have a lot of concerns about it as production-worthy, especially because you're manually firing browser native events during periods that people generally wouldn't expect them to fire.
I'm saying this as someone that had to write a full clientside routing system for a bespoke web app - routing is very hard and full of edge cases.
There's one thing, you should redirect all the pages to one single endpoint in server side order to use "pushState". Otherwise it will return 404 when you hit the refresh button. If you don't own a server, you can support routing with hashtag "#" and listen to "onhashchange" event instead of "popstate".
Also, if you would like to support nested and dynamic routes (it's not possible with that code snippet in the github repository since it just checks like `path===currentPath`), you might look at the following solution:
https://github.com/fatih-erikli/universal-router/blob/main/u...
I use that solution in server-side and client-side so it works like Nextjs.
Could you explain this one a bit more / maybe some example code I could borrow from? :)
I made one myself that was absolutely the simplest I could go for[0]
Why is routing, of all things, in such need of constant development? I say this as someone that’s made SPAs with Vue a lot and occasionally React.
However this makes some people vocally angry, because in theory a developer should know every single packages in the world, but never the internals. Only people of a higher class or caste should be allowed to release stuff to the world. The rest are peasants. How dare they. /s
The reason people say this is because the scale of the problem is much, much worse in front-end javascript than in most other language communites and it's a real problem for people attempting to get to grips with best practice.
If your explanation was correct then we'd see the same problem everywhere at the same scale but there's something about front-end javascript that seems to exacerbate this issue.
About being uncharitable: IMO this constant mischaracterization of frontend is the real uncharitable thing here.
I also do a lot of backend work and even the most "traditionally stable" programming languages (or frameworks) have a lot of churn in libraries, frameworks, patterns, toolchains, deployment methods and architectures. Which is "fine", that's how programming works!
But heck, I worked recently on a 8 year old Rails app that had four different methods/libraries/frameworks being used to wrap business code. But in the frontend that would be extremely rare.
If anything the frontend has been quite stable for the last 7 years or so when we settled on mostly React or Vue. The only real big change was Hooks, and even that was incremental and was an attempt to solve real problems in the ecosystem.
You don't need any of the libraries in the article to make a good React app/website. Period. No, not even React-Router or Redux. And those two are probably the only ones that you'll see lots of companies/devs that actually ship stuff using. And both of them are not even that complicated, as is demonstrated in this article for the Router. Redux is also quite simple (hence why there's so many alternative implementations).
If anything the ones doing the gaslighting are the ones saying that we devs need to use ultra-complex build systems and gigantic libraries to make a "real app". It's the gaslighting of saying simple architectures "won't ever work" mixed with machismo of "real programmers do X".
Sure, if you want the complexity that comes with using those "must-use" libraries, feel free to have it. I won't say you're wrong by doing so. Maybe you REALLY need all 14 of them, although I doubt it. But please stop assuming that everyone is doing the same or has to. Nor try to deny the reality of me and lots of others getting things done without "14 must use libraries". Because that is the definition of gaslighting.
The React ecosystem looks like the crypto / nft space to outsiders. Nobody seems to agree on anything, no two projects look alike, it’s just this endless loop of people reinventing the same core bits over and over again for the past ten years.
I’m happy for you that this hasn’t been you experience but don’t tell me that the React ecosystem is stable. The reputation of the React ecosystem for many years has famously been that it’s a house of cards.
You, on the other hand seems to be claiming that the only reality that matters is the one that matches your own biases about an ecosystem that yourself seems to only participate from the periphery of SEO-spam blog posts.
It's funny that you're started by calling me a gaslighter when it is exactly what you're doing.
Claiming that React looks like Crypto/NFT only makes sense from anyone taking advice from such blogs.
The core bits have definitely not been reinvented, as the library itself hasn't really changed much to the outside in eight years other than the adoption of hooks and a few incremental advances.
Please spare the condescending "I’m happy for you" part. You're clearly not part of the industry that is using React in a stable way, and you don't seem to want to be part of. Even if your description of the React ecosystem were true, you're clearly on a personal vendetta here and has no intention to see anything change for the better.
we do. Every C app implements its own linked list or hash table library. The entire Scheme community is nothing but toy interpreters of various stages of completeness (you can tell a project is serious when they implement call/cc). How many game engines do you think exist? It's a meme that game devs like to spend more time on their pet game engine than actually making their game. How many ORMs do you think exist for <language-of-choice>? At least half a dozen. At least. For any given language. Python, Ruby, Go[1]. ORMs, in particular, seem to get created over and over again. Probably because they are trivial to implement and allows one to voice their opinions on SQL abstraction (bike shedding).
There's nothing special about JS, but there is something special about being forced to use JS.
This won't be popular: but tech self-proclaimes itself as innovative and then wastes time mucking around reinventing wheel after wheel after wheel.
Sure, the self-edu is helpful. Of course. But does that always have to a wheel we've seen before going down a road we've been down before?
Note: Not trolling or ranting. Just looking for new thoughts and ideas.
In this same thread there is a comment about how this little code snippet "throws away backward compatibility, plus everything else".
New tech has to have backwards compatibility to even be taken serious by most people. But backwards compatibility requires not only reinventing the wheel, but also having it being on the exact same size and material as before so it fits where it was used.
The only way to satisfy both of the groups above is to evolve tech that already exists and implement new findings in already existing libraries, but that will also enrage a lot of people. An example: React Hooks.
Don't forget React is a Facebook product.
As someone coming from an Angular shop, I started a new job a few months ago that uses React on the frontend. My main gripe so far is there is no one "way" to do X in a react-based app. For an enterprise with lots of developers, Angular seems like a better fit - unless you want to spend months coming up with standards. Angular has its own issues of course - it's much more rigid and has a steeper learning curve.
Now there are solutions comparable to Vue/etc which use React -- Next.js, Create React App, etc. Comparisons, tradeoffs, and criticisms are totally fair there. Just don't blame React for churn in third party implementations of features outside of its purview.
As a side note, I love that React is so narrowly scoped. It lets the community build on, experiment with, and coalesce around amazing solutions to other big features outside of React's scope. That Darwinian approach has created broad front end solutions that to me are a joy to use compared to similarly featured frameworks like Angular.
Sadly, for reasons that remain a mystery to me it’s one of those things like Apple or crypto where some of its loudest fans have decided to make it a part of their identity rather than a tool they enjoy using.
Other frameworks have adopted some of those concepts in different ways, and it works quite well. Other technologies have other ways of solving the same problems, and they also work! Of course, it is absolutely not perfect and doesn't solve "all problems", but that's how mature technologies generally are.
Modern browsers have tried to provide a competing technology with WebComponents, but despite being native to the platform and embraced en masse not only by browser makers but by large companies, it hasn't taken off like React. Please take a time to reflect as to why something that is "there" isn't as widely used as something that requires a 50kb library to even start. And no, it's not a conspiracy, nor it is marketing. There are very good technical reasons for that.
About the "fans" part: please look at the mirror. You seem to be projecting a lot here. People use React and other similar libraries because it gets the job done. A lot of your posts seem to be about competing technologies, or are anti-Apple, or anti-something. Criticism is perfectly fine, but you're the one making hating something part of your identity and the projection is blurring your ability to empathise with what others are saying.
Because requirements change. In the last few years there was a lot of churn in React due to the introduction of async rendering and server-side rendering, and React routers were strongly affected by both of those changes.