🚀
ShipClojure
  • README
  • Development
    • Getting Started
    • REPL Workflow
    • AI Development with ShipClojure
    • Getting Updates
    • Formatting code
    • ShipClojure Guiding Principles
  • Backend
    • Migrations
    • Secrets
    • Routing
    • ShipClojure Blog
    • Email
  • Frontend
    • UIx + re-frame
    • HTTP Requests with Re-frame
    • Frontend Navigation with Re-frame
    • Toast Notifications
    • Icons
  • Server Side Rendering
    • Static/Landing pages
  • Auth
    • How Auth works
    • Oauth2 providers
  • Deployment
    • Deployment
  • Decisions
    • 001 - Cookie Sessions
    • 002 - Single Page Application Architecture
    • 003 - Re-frame instead of Refx
    • 003 - Move from cookie sessions to JWT Access + refresh tokens
Powered by GitBook
On this page
  • Overview
  • Navigation Events
  • Basic Navigation
  • Using :router/navigate
  • Navigation with Parameters
  • Direct Navigation Effect
  • Handling Navigation Results
  • Common Navigation Patterns
  • Navigation after Success
  • Conditional Navigation in an Event Handler
  • Redirecting to Login
  • Route Definition
  • SEO Considerations
  • Route Redirects
  • How It Works
  • Common Use Cases
  • Implementation Details
  • Example Usage
  • Basic Navigation Button
  • Navigation After Form Submission
  1. Frontend

Frontend Navigation with Re-frame

PreviousHTTP Requests with Re-frameNextToast Notifications

Last updated 1 month ago

This document covers how to handle client-side navigation in the application using Re-frame events.

Overview

The application uses for routing in combination with 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:

  1. :router/navigate - A regular event that triggers navigation

  2. :router/navigate! - An effect that performs immediate navigation

  3. :router/redirect - An event triggered when a route has a :redirect-to property

Basic Navigation

Using :router/navigate

The 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

  1. Define a route with the :redirect-to property in src/cljc/saas/common/routes.cljc:

["/settings"
 ["" (merge {:name :routes.settings/index
             :redirect-to :routes.settings/profile}
            #?(:cljs {:view #($ :div)}))]
 ["/profile" ...]]
  1. When a user navigates to /settings, the router detects the :redirect-to property and automatically redirects to the specified route (:routes.settings/profile in this example).

  2. The redirection is handled by the :router/redirect event, which is dispatched when a route with :redirect-to is matched.

Common Use Cases

  • Default pages for sections (e.g., redirecting /settings to /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 handling

  • src/cljs/saas/db/events.cljs: Defines the navigation events and effects

  • src/cljc/saas/common/routes.cljc: Defines the application routes

  • src/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
       )))
Reitit
Re-frame