Fork me on GitHub
#reitit
<
2023-01-03
>
Mario Trost11:01:52

I'm trying to migrate a big frontend (re-frame) app to reitit and struggling with programmatically navigating to a new path (what useNavigate and navigate functions do in react-router/`remix`). Details in thread

Mario Trost11:01:53

It's on a catch-all route apps/*app-path, what I tried: 1. Following the repo example frontend-re-frame I used ::navigate! effect handler calling rfe/push-state with route name, path-params and query-params (after matching by path for the new route). Problem: the catch-all path-params is {:app-path: "some/app"} where the / becomes URI encoded to some%2Fapp resulting in the wrong browser URL. 2. Tried to manually call (.pushState js/window.history nil {} "/apps/some/app") doesn't push new history entry? I cannot navigate back. When using fragment router this manual push and navigating back does work. 3. Also tried to manually call -on-navigate similar to how it's done in the reitit code base for push-state, no luck either:

;; pushState and replaceState don't trigger popstate event so call on-navigate manually
     (.pushState js/window.history nil "" (-href history path))
     (-on-navigate history path))

Mario Trost11:01:06

At the point where I think I misunderstand something fundamentally, because navigating to a new path should be simpler? Navigating by anchor tags works seamlessly. Any suggestions?

Mario Trost11:01:47

before I forget: overall reitit is great, thank you for creating it! 😄

robert-stuttaford12:01:04

suggest getting their example code running locally, and then once you've seen it working with your own eyeballs, altering + testing it bit by bit until it's reproducing the situation you're after. somewhere along that journey, you'll find your bug!

robert-stuttaford12:01:13

(and you'll learn a ton along the way)

Mario Trost12:01:33

Oh should have mentioned: I already did that

Mario Trost12:01:24

When 2. didn't work I was convinced my setup was wrong so I tried the minimal example from the repository

Mario Trost12:01:57

With the setup from the respository:

(defn init-routes! []
  (js/console.log "initializing routes")
  (rfe/start!
   router
   on-navigate
   {:use-fragment true}))
I can do (.pushState js/window.history nil {} "/sub-page2") and history stack is, but when I change the init code to {:use-fragment false} that doesn't work anymore

Mario Trost13:01:11

Some code example (from the re-frame example, commented what I added and what does not work as I would expect):

Mario Trost13:01:19

(def routes
...
   ["sub-page2"
    {:name      ::sub-page2
     :view      sub-page2
     :link-text "Sub-page 2"}]

   ;; Added this route
   ["sub-page3/*page-path"
    {:name      ::sub-page3
     :view      sub-page3
     :link-text "Sub-page 3"}]])

(def router
  (rf/router
   routes
   {:data {:coercion rss/coercion}}))

;; Effects
(re-frame/reg-fx
 :push-state
 (fn [route]
   (js/console.error "ROUTE" route)
   (apply rfe/push-state route)))

(re-frame/reg-event-fx
 ::push-state
 (fn [_ [_ & route]]
   ;; See `navigate` effect in routes.cljs
   {:push-state route}))

;; ...and this handler
(re-frame/reg-event-fx
   ::navigate-by-path
  (fn [_ [_ new-path]]
    (let [match       (r/match-by-path router new-path)
          route-name  (get-in match [:data :name])
          path-params (:path-params match)]
      {:fx [[:dispatch [::push-state route-name path-params]]]})))

(comment
  ;; This work
  (re-frame/dispatch [::push-state ::sub-page2])

  ;; This works too
  (re-frame/dispatch [::navigate-by-path "/sub-page2"])

  ;; This navigates but messes up the browser URL to: /sub-page3/some%2Fapp
  (re-frame/dispatch [::navigate-by-path "/sub-page3/some/app"])


  ;; works only with {:use-fragment true}, else history stack is broken
  (.pushState js/window.history nil "" "/sub-page3/some/app")
  )

Mario Trost13:01:32

How can I navigate by to a catch-all route programmatically when 1. retitit.frontend.easy/push-state does not take the path as an argument and uri encodes path-params 2. (.pushState js/window.history nil "" "/sub-page3/some/app") only works when using fragment router and not with {:use-fragment false} (is that a bug?)

Mario Trost14:01:33

I found that and tried it, but no luck 😞 But the issue I meant with 2 is that navigating back doesn't work anymore (but {:use-fragment true} works)

Mario Trost14:01:53

Let me do a short screengrab to show what I mean

Mario Trost14:01:16

ooookay that number 2 works now: I can navigate back 😄 Then I can just manually call (-on-navigate history path) as you indicated and I mentioned in my first message (number 3). Here is a short clip what my initial stumbling block was: retitit.frontend.easy/push-state uses match-by-name! which encodes the slash while match-by-path does not.

Mario Trost14:01:32

But I guess it doesn't make sense how I wanted to do it: I matched by path on the router to get the new route and used that to call push-state which itself does again search for the matched route by using match-by-name. I go the number 2 route and pushState plus -on-navigate myself (again... was stuck trying to do that for quite some time this morning 😄 )

Mario Trost15:01:48

Worked! ...and found my error: I tried it only in the repl while having 2 browser windows open but only the one I wasn't looking at received the changes 😞

Mario Trost15:01:33

Definitely learned a lot, thanks for rubber ducking, and apologies for the time taken