Skip to content

re-frame.adapter.helix

re-frame.adapter.helix binds re-frame2 to the Helix React substrate. It ships as its own artefact (day8/re-frame2-helix) and depends on re-frame.core, never the reverse. A Helix app requires this namespace and passes its adapter Var into (rf/init! ...). It exposes:

  • the Helix-shaped hooks use-subscribe, use-frame, use-resource-lease, and use-current-frame;
  • the frame-provider (SCOPE) + frame-root (ENSURE) components;
  • the wrap-view view-wrapping seam;
  • the adapter spec.

The substrate-agnostic carry and scoping primitives (capture-frame, with-frame, with-new-frame) live on re-frame.core. The surface mirrors the UIx adapter one-for-one. For choosing between substrates, see Use UIx, Helix, or reagent-slim.

(:require [re-frame.adapter.helix :as helix-adapter])

Initialisation

adapter

  • Kind: Var (map)
  • Signature:
    {:kind                      :rf.adapter/helix
     :make-state-container      
     :read-container            
     :replace-container!        
     :subscribe-container       
     :make-derived-value        
     :render                    
     :render-to-string          
     :register-context-provider 
     :flush-render!             
     :dispose-adapter!          }
    
  • Description: The adapter spec passed to (rf/init! ...). Implements the same adapter contract as the Reagent and UIx adapters. :kind is :rf.adapter/helix.

Hooks

use-subscribe

  • Kind: Helix hook (function)
  • Signature:
    (use-subscribe query-v)  current sub value
    (use-subscribe frame-kw query-v)  current sub value
    
  • Description: The hook-shaped equivalent of subscribe for Helix components. Returns the current sub value and re-renders the calling component when it changes.
  • The 1-arg form resolves the frame through the surrounding scope: dynamic-var first, then React context. It raises :rf.error/no-frame-context when no frame is in scope.
  • The 2-arg form pins an explicit frame id.

use-frame

  • Kind: Helix hook (function)
  • Signature:
    (use-frame)  {:frame  :dispatch  :dispatch-sync  :subscribe }
    
  • Description: Returns the frame ops map for the ambient frame — exactly what (rf/capture-frame) returns (the frame-locked ops map), captured in hook position. This is how a Helix component gets hold of dispatch (and the other frame-locked ops) without auto-injection: destructure dispatch off it and close over that.

capture-frame is the hold primitive; reg-view injection (Reagent) and use-frame (UIx / Helix) are its two ergonomic spellings — one primitive, three faces.

  • Frame resolution matches use-subscribe: dynamic-var scope first, then the surrounding frame-provider / frame-root via React context. It raises :rf.error/no-frame-context when neither is in scope.
  • The returned map is reference-stable across re-renders for the same resolved frame (safe in effect deps and child props); a provider swap re-renders the caller and yields a map locked to the new frame.
  • No options map, no variants — for an explicit frame, call (rf/capture-frame frame-id) directly.
  • Example:
    (defnc counter-buttons []
      (let [count              (helix-adapter/use-subscribe [:counter/value])
            {:keys [dispatch]} (helix-adapter/use-frame)]
        (d/button {:on-click #(dispatch [:counter/inc])} "+")))
    

use-resource-lease

  • Kind: Helix hook (function)
  • Signature:
    (use-resource-lease descriptor)  nil
    (use-resource-lease descriptor opts)  nil
    
  • Description: Holds a resource liveness lease for the calling component's mounted lifetime. On mount it dispatches :rf.resource/ensure with an app-minted [:lease …] owner; on unmount it dispatches :rf.resource/release-owner for that lease. Returns nil: it manages liveness only. Read the data with use-subscribe on a [:rf.resource/*] query.
  • descriptor — resource-instance identity {:resource … :scope … :params …}.
  • opts:cause (recorded on the ensure; defaults to [:lease :mount]) and :frame (pin to an explicit frame id, bypassing ambient resolution).
  • Without :frame, the frame resolves through the same chain as use-subscribe, raising :rf.error/no-frame-context when no frame is in scope.
  • Inert unless the resources artefact (day8/re-frame2-resources) is on the classpath to register the resource events.
  • Example:
    (use-resource-lease {:resource :my/feed :scope :rf.scope/global :params {:page 0}})
    

use-current-frame

  • Kind: Helix hook (function)
  • Signature:
    (use-current-frame)  frame-kw, or :rf.frame/no-provider when no boundary is above
    
  • Description: A raw React-context read of the frame keyword supplied by the surrounding frame-provider (SCOPE) or frame-root (ENSURE) — both install the one shared React context. When neither sits above, it returns :rf.frame/no-provider — never a synthesised default. It consults the React-context tier only, not the dynamic-var tier; for the full resolution chain, use (rf/current-frame-id).

NOT USED — no call sites found in implementation/, examples/, or tools/.

Components

frame-provider

  • Kind: Helix component (function — SCOPE-only)
  • Signature:
    ($ helix-adapter/frame-provider {:frame :session} child)   ;; SCOPE an existing frame
    
  • Description: The Helix-shaped SCOPE-only frame provider (rf2-nyea0r split — roots ensure; providers scope; for create-if-absent, use frame-root). Scopes an already-created frame; creates nothing. Children ride the $ trailing-args channel (there is no :children prop-map key).
  • Fails loud if the frame is absent (:rf.error/frame-provider-frame-absent). A nil :frame raises :rf.error/no-frame-context; a non-keyword raises :rf.error/bad-frame-provider-arg; an :id (the ENSURE key) raises :rf.error/frame-provider-given-id.
  • Example:
    ($ helix-adapter/frame-provider {:frame :session}
       ($ dashboard))
    

frame-root

  • Kind: Helix component (function — ENSURE, a commit-owned two-pass boundary)
  • Signature:
    ($ helix-adapter/frame-root {:id :session :images [session-image]} child)   ;; ENSURE create-if-absent / reuse
    
  • Description: The Helix-shaped ENSURE component (rf2-nyea0r split). Creates the named frame if absent, or reuses it without re-seeding if present; never destroys the frame on unmount. Accepts make-frame opts. A missing/nil/non-keyword :id raises :rf.error/frame-root-missing-id. Children ride the $ trailing-args channel.
  • Commit-owned two-pass: the create/seed runs in a client useLayoutEffect (at commit), not during render — children render only after the frame is live, and a render React discards before commit creates + seeds nothing (no ghost frame).
  • Re-mounting under the same :id (hot reload, React StrictMode dev double-invoke) neither destroys durable state nor re-runs :initial-events. A mounted :id/opts change raises :rf.error/frame-root-reconfigured; a stray :frame raises :rf.error/frame-root-given-frame.
  • Example:
    ;; create the frame on first mount, seed it once via :initial-events,
    ;; reuse (no re-seed) on hot-reload re-mount.
    ($ helix-adapter/frame-root {:id :app :initial-events [[:counter/initialise]]}
       ($ counter-app))
    

View wrapping

wrap-view

  • Kind: function
  • Signature:
    (wrap-view id metadata user-fn)  wrapped fn
    
  • Description: Adapter-side source-coord injection. Wraps user-fn in a function component that injects data-rf2-source-coord on the rendered root DOM element in debug builds. Production builds elide the injection and return user-fn unchanged. The returned fn has the same call signature as user-fn and is suitable as a Helix component head.
  • Example:
    ;; Code-gen / scaffolding seam: wrap a component head so its root DOM element
    ;; carries data-rf2-source-coord (dev only; elided in production builds).
    (def wrapped-row
      (helix-adapter/wrap-view ::row {:line 42 :column 7}
                               (fn [_props] (d/div "row"))))
    

Test and SSR seams

flush-views!

  • Kind: function
  • Signature:
    (flush-views!)
    (flush-views! f)
    
  • Description: Wraps React's act() for tests. The 1-arity form calls (act f); the 0-arity form flushes pending effects. Returns nil.
  • Example:
    ;; Test-only: flush pending renders synchronously, returns nil.
    (helix-adapter/flush-views!)               ;; 0-arity: drain queued renders + effects
    (helix-adapter/flush-views! (fn [] nil))   ;; 1-arity: run the thunk inside act()
    

set-hiccup-emitter!

  • Kind: function
  • Signature:
    (set-hiccup-emitter! f)
    
  • Description: Installs a render-tree → HTML fn. This is the Helix side of the SSR late-bind seam, at parity with the Reagent and UIx adapters. Normally you don't call this directly; requiring re-frame.ssr wires the emitter for you. Pass nil to reset.
  • Example:
    ;; SSR: install a render-tree → HTML emitter (normally wired for you by
    ;; requiring re-frame.ssr). Pass nil to reset.
    (helix-adapter/set-hiccup-emitter! (fn [tree _opts] (str tree)))
    (helix-adapter/set-hiccup-emitter! nil)
    

Worked example

(:require [re-frame.core :as rf]
          [re-frame.adapter.helix :as helix-adapter]
          [helix.core :refer [defnc]]
          [helix.dom :as d])

(rf/init! helix-adapter/adapter)

(defnc cart-row [{:keys [item]}]
  (let [count (helix-adapter/use-subscribe [:cart/count])]
    (d/tr
      (d/td (:name item))
      (d/td count))))