Frontend Navigation with Re-frame
This document covers how to handle client-side navigation in the application using Re-frame events.
Overview
The application uses Reitit for routing in combination with Re-frame for state management. This provides a powerful and flexible way to handle navigation in your Single Page Application (SPA).
Navigation Events
There are two main navigation events available:
:router/navigate- A regular event that triggers navigation:router/navigate!- An effect that performs immediate navigation:router/redirect- An event triggered when a route has a:redirect-toproperty
Basic Navigation
Using :router/navigate
:router/navigateThe most common way to trigger navigation is with the :router/navigate event:
(rf/dispatch [:router/navigate {:route ::routes/dashboard}])This will navigate the user to the dashboard route defined in your routes configuration.
Navigation with Parameters
You can include path parameters and query parameters in your navigation:
(rf/dispatch [:router/navigate
{:route ::routes/user-profile
:path-params {:user-id "123"}
:query-params {:tab "settings"}}])This will navigate to a URL like /user/123?tab=settings (assuming your route is defined as something like /user/:user-id).
Direct Navigation Effect
In some cases, you might want to use the navigation effect directly in a custom event handler:
(rf/reg-event-fx
::my-custom-event
(fn [{:keys [db]} [_ some-data]]
;; Do something with the data
{:db (assoc db :some-key some-data)
:router/navigate! {:route ::routes/success-page}}))The :router/navigate! effect performs the navigation immediately as a side effect.
Handling Navigation Results
When a navigation occurs, the :router/navigated event is dispatched with the new route details. This event updates the application state with the current route information and sets any SEO-related metadata.
The route information is stored in the app db under :router/current-route and can be accessed with a subscription:
(rf/subscribe [:router/current-route])Common Navigation Patterns
Navigation after Success
A common pattern is to navigate after an API call succeeds:
(rf/dispatch
[:http/api
{:method :post
:url :some/endpoint
:body data
:on-success [:router/navigate {:route ::routes/success-page}]}])Conditional Navigation in an Event Handler
(rf/reg-event-fx
::process-auth
(fn [{:keys [db]} [_ auth-result]]
(if (:success auth-result)
{:db (assoc db :auth auth-result)
:router/navigate! {:route ::routes/dashboard}}
{:db (assoc db :auth-error (:error auth-result))})))Redirecting to Login
When an unauthorized action is attempted, it's common to redirect to the login page:
(rf/reg-event-fx
::handle-unauthorized
(fn [{:keys [db]} _]
{:db (dissoc db :auth)
:router/navigate! {:route ::routes/login}}))Route Definition
Routes are defined in src/cljc/saas/common/routes.cljc using Reitit's routing syntax. Here's a simplified example:
(defn app-routes []
[""
["/" {:name ::routes/index
:seo/title "Home"}]
["/dashboard" {:name ::routes/dashboard
:seo/title "Dashboard"
:view dashboard/dashboard-page}]
["/user/:user-id" {:name ::routes/user-profile
:seo/title "User Profile"
:view user/profile-page
:parameters {:path {:user-id string?}
:query {:tab string?}}}]])SEO Considerations
When navigating to a new route, the :router/navigated event will automatically update the page title and meta description based on the :seo/title and :seo/description properties defined in your route data.
Route Redirects
The application supports automatic redirects through the :redirect-to route property. This allows you to define routes that automatically redirect to another route when accessed.
How It Works
Define a route with the
:redirect-toproperty insrc/cljc/saas/common/routes.cljc:
["/settings"
["" (merge {:name :routes.settings/index
:redirect-to :routes.settings/profile}
#?(:cljs {:view #($ :div)}))]
["/profile" ...]]When a user navigates to
/settings, the router detects the:redirect-toproperty and automatically redirects to the specified route (:routes.settings/profilein this example).The redirection is handled by the
:router/redirectevent, which is dispatched when a route with:redirect-tois matched.
Common Use Cases
Default pages for sections (e.g., redirecting
/settingsto/settings/profile)Legacy URLs that should redirect to new locations
Creating logical groupings of routes with a default landing page
Implementation Details
The navigation system is implemented in:
src/cljs/saas/core.cljs: Sets up the router and initializes route handlingsrc/cljs/saas/db/events.cljs: Defines the navigation events and effectssrc/cljc/saas/common/routes.cljc: Defines the application routessrc/cljs/saas/routing.cljs: Handles route matching and redirection logic
Example Usage
Basic Navigation Button
(defui nav-button []
($ ui/button
{:on-click #(rf/dispatch [:router/navigate {:route ::routes/dashboard}])}
"Go to Dashboard"))Navigation After Form Submission
(defui signup-form []
(let [form-data (use-state {:email "" :password ""})
handle-submit (fn [e]
(.preventDefault e)
(rf/dispatch [:auth/register
@form-data
;; Navigate after successful registration
{:on-success [:router/navigate {:route ::routes/onboarding}]}]))]
($ :form {:on-submit handle-submit}
;; Form elements here
)))Last updated