Merge pull request #438 from metosin/frontend-fixes

Frontend fixes
This commit is contained in:
Tommi Reiman 2020-10-19 10:42:45 +03:00 committed by GitHub
commit f2f7d3a428
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 50 deletions

View file

@ -18,6 +18,11 @@ We use [Break Versioning][breakver]. The version numbers follow a `<major>.<mino
## 0.5.7 (2020-10-18) ## 0.5.7 (2020-10-18)
### `reitit-frontend`
- `reitit.frontend.easy/start!` now correctly removes old event listeners
when called repeatedly (e.g. with hot code reload workflow)
([#438](https://github.com/metosin/reitit/pull/438))
* updated deps: * updated deps:
```clj ```clj

View file

@ -33,7 +33,7 @@
{:builds {:builds
[{:id "dev" [{:id "dev"
:source-paths ["src/cljs"] :source-paths ["src/cljs"]
:figwheel {:on-jsload "frontend-re-frame.core/mount-root"} :figwheel true
:compiler {:main frontend-re-frame.core :compiler {:main frontend-re-frame.core
:output-to "resources/public/js/compiled/app.js" :output-to "resources/public/js/compiled/app.js"
:output-dir "resources/public/js/compiled/out" :output-dir "resources/public/js/compiled/out"

View file

@ -2,12 +2,9 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset='utf-8'> <meta charset='utf-8'>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script src="js/compiled/app.js"></script> <script src="js/compiled/app.js"></script>
<script>frontend_re_frame.core.init();</script>
</body> </body>
</html> </html>

View file

@ -9,30 +9,27 @@
;;; Events ;;; ;;; Events ;;;
(re-frame/reg-event-db (re-frame/reg-event-db ::initialize-db
::initialize-db (fn [db _]
(fn [_ _] (if db
{:current-route nil})) db
{:current-route nil})))
(re-frame/reg-event-fx (re-frame/reg-event-fx ::push-state
::navigate (fn [db [_ & route]]
(fn [db [_ & route]] {:push-state route}))
;; See `navigate` effect in routes.cljs
{::navigate! route}))
(re-frame/reg-event-db (re-frame/reg-event-db ::navigated
::navigated (fn [db [_ new-match]]
(fn [db [_ new-match]] (let [old-match (:current-route db)
(let [old-match (:current-route db) controllers (rfc/apply-controllers (:controllers old-match) new-match)]
controllers (rfc/apply-controllers (:controllers old-match) new-match)] (assoc db :current-route (assoc new-match :controllers controllers)))))
(assoc db :current-route (assoc new-match :controllers controllers)))))
;;; Subscriptions ;;; ;;; Subscriptions ;;;
(re-frame/reg-sub (re-frame/reg-sub ::current-route
::current-route (fn [db]
(fn [db] (:current-route db)))
(:current-route db)))
;;; Views ;;; ;;; Views ;;;
@ -41,7 +38,7 @@
[:h1 "This is home page"] [:h1 "This is home page"]
[:button [:button
;; Dispatch navigate event that triggers a (side)effect. ;; Dispatch navigate event that triggers a (side)effect.
{:on-click #(re-frame/dispatch [::navigate ::sub-page2])} {:on-click #(re-frame/dispatch [::push-state ::sub-page2])}
"Go to sub-page 2"]]) "Go to sub-page 2"]])
(defn sub-page1 [] (defn sub-page1 []
@ -55,10 +52,10 @@
;;; Effects ;;; ;;; Effects ;;;
;; Triggering navigation from events. ;; Triggering navigation from events.
(re-frame/reg-fx
::navigate! (re-frame/reg-fx :push-state
(fn [route] (fn [route]
(apply rfe/push-state route))) (apply rfe/push-state route)))
;;; Routes ;;; ;;; Routes ;;;
@ -104,15 +101,15 @@
(def router (def router
(rf/router (rf/router
routes routes
{:data {:coercion rss/coercion}})) {:data {:coercion rss/coercion}}))
(defn init-routes! [] (defn init-routes! []
(js/console.log "initializing routes") (js/console.log "initializing routes")
(rfe/start! (rfe/start!
router router
on-navigate on-navigate
{:use-fragment true})) {:use-fragment true}))
(defn nav [{:keys [router current-route]}] (defn nav [{:keys [router current-route]}]
[:ul [:ul
@ -141,13 +138,12 @@
(enable-console-print!) (enable-console-print!)
(println "dev mode"))) (println "dev mode")))
(defn mount-root [] (defn init []
(re-frame/clear-subscription-cache!) (re-frame/clear-subscription-cache!)
(re-frame/dispatch-sync [::initialize-db])
(dev-setup)
(init-routes!) ;; Reset routes on figwheel reload (init-routes!) ;; Reset routes on figwheel reload
(reagent/render [router-component {:router router}] (reagent/render [router-component {:router router}]
(.getElementById js/document "app"))) (.getElementById js/document "app")))
(defn ^:export init [] (init)
(re-frame/dispatch-sync [::initialize-db])
(dev-setup)
(mount-root))

View file

@ -26,11 +26,14 @@
(let [path (-get-path this)] (let [path (-get-path this)]
(when (or (= goog.events.EventType.POPSTATE (.-type e)) (when (or (= goog.events.EventType.POPSTATE (.-type e))
(not= @last-fragment path)) (not= @last-fragment path))
(-on-navigate this path))))] (-on-navigate this path))))
;; rfe start! uses first on-navigate call to store the
;; instance so it has to see the instance with listeners.
this (assoc this
:popstate-listener (gevents/listen js/window goog.events.EventType.POPSTATE handler false)
:hashchange-listener (gevents/listen js/window goog.events.EventType.HASHCHANGE handler false))]
(-on-navigate this (-get-path this)) (-on-navigate this (-get-path this))
(assoc this this))
:popstate-listener (gevents/listen js/window goog.events.EventType.POPSTATE handler false)
:hashchange-listener (gevents/listen js/window goog.events.EventType.HASHCHANGE handler false))))
(-stop [this] (-stop [this]
(gevents/unlistenByKey popstate-listener) (gevents/unlistenByKey popstate-listener)
(gevents/unlistenByKey hashchange-listener) (gevents/unlistenByKey hashchange-listener)
@ -115,11 +118,12 @@
(when (.hasFragment uri) (when (.hasFragment uri)
(str "#" (.getFragment uri))))] (str "#" (.getFragment uri))))]
(.pushState js/window.history nil "" path) (.pushState js/window.history nil "" path)
(-on-navigate this path))))))] (-on-navigate this path))))))
this (assoc this
:listen-key (gevents/listen js/window goog.events.EventType.POPSTATE handler false)
:click-listen-key (gevents/listen js/document goog.events.EventType.CLICK ignore-anchor-click))]
(-on-navigate this (-get-path this)) (-on-navigate this (-get-path this))
(assoc this this))
:listen-key (gevents/listen js/window goog.events.EventType.POPSTATE handler false)
:click-listen-key (gevents/listen js/document goog.events.EventType.CLICK ignore-anchor-click))))
(-on-navigate [this path] (-on-navigate [this path]
(on-navigate (rf/match-by-path router path) this)) (on-navigate (rf/match-by-path router path) this))
(-stop [this] (-stop [this]

View file

@ -12,6 +12,8 @@
["foo" ::foo] ["foo" ::foo]
["bar/:id" ::bar]])) ["bar/:id" ::bar]]))
;; TODO: Only tests fragment history, also test HTML5?
(deftest easy-history-routing-test (deftest easy-history-routing-test
(when browser (when browser
(gevents/removeAll js/window goog.events.EventType.POPSTATE) (gevents/removeAll js/window goog.events.EventType.POPSTATE)
@ -24,7 +26,8 @@
(fn on-navigate [match history] (fn on-navigate [match history]
(let [url (rfh/-get-path history)] (let [url (rfh/-get-path history)]
(case (swap! n inc) (case (swap! n inc)
1 (do (is (= "/" url) 1 (do (is (some? (:popstate-listener history)))
(is (= "/" url)
"start at root") "start at root")
(rfe/push-state ::foo)) (rfe/push-state ::foo))
2 (do (is (= "/foo" url) 2 (do (is (= "/foo" url)
@ -41,7 +44,24 @@
(.back js/window.history)) (.back js/window.history))
6 (do (is (= "/" url) 6 (do (is (= "/" url)
"go back after replace state") "go back after replace state")
(rfh/stop! @rfe/history)
(done)) ;; Reset to ensure old event listeners aren't called
(is false "extra event")))) (rfe/start! router
(fn on-navigate [match history]
(let [url (rfh/-get-path history)]
(case (swap! n inc)
7 (do (is (= "/" url)
"start at root")
(rfe/push-state ::foo))
8 (do (is (= "/foo" url)
"push-state")
(rfh/stop! @rfe/history)
(done))
(do
(is false (str "extra event 2" {:n @n, :url url}))
(done)))))
{:use-fragment true}))
(do
(is false (str "extra event 1" {:n @n, :url url}))
(done)))))
{:use-fragment true}))))) {:use-fragment true})))))