I also basically re-invent a tiny 50 line router for every web project. Hardly ever go beyond "Map URL -> Page" and "Map URL + Parameters -> Page with properties", and when you do, knowing 100% how it works helps a lot.
I also agree it isn't a hard problem, but personally I'd say you got the flow the wrong way around. You don't want to "synchronize state to the page URL" but rather treat the page URL as something you create state from, so it works both when you navigate there by pressing anchor tags, or the user manually enters the URL, and it gets a bit easier to manage.
Basically, URL is the top-level state, and from there you derive what page, then render that page, rather than the other way around.
Yeah, implementing it with data flowing one-way only from the URL to the state is cleaner.
Conceptually, however, I prefer to think of my state being at the center of things. I mean, that's where I define (via types) what the state is. The URL is just one serialization of that state that is convenient to use in a web browser (making it work with links, back/forth buttons, etc). Maybe in another environment another serialization would be needed. Or maybe no serialization could be needed at all (making it a memory router).