-
Notifications
You must be signed in to change notification settings - Fork 2
Configuration
Reflet configuration can be outlined as such:
- Minimum Config
- Options
- Descriptions
- Development and Production Builds
- Debugger
- React Peer Dependency
A fully functioning system, minus the debugger, can be configured in a single event:
(require '[reflet.core :as f])
(f/disp-sync [::f/config])
At minimum this event provides the Reflet graph db with a set of unique attributes (see the References and Application Design and the Multi Model DB documents to see what unique attributes are and how they are used).
This event must be processed before any graph db operations occur, or any graph pull subscriptions are created. A typical hot-reloadable development setup might look like:
(ns my.client
(:require [my.client.ui :as ui]
[re-frame.core :as f*]
[reagent.dom :as dom]
[reflet.core :as f]))
(defn render!
"Called on each hot reload."
[]
(f*/clear-subscription-cache!)
(some->> "container"
(.getElementById js/document)
(dom/render [ui/app])))
(defn init!
"Called only once on application start."
[]
(f/disp-sync [::f/config]) ; <- config only happens once, must be synchronous!
(render!)) ; <- then the application runs
If you need something beyond the defaults, you can pass a config map
to the Reflet's ::f/config
event:
(f/disp-sync [::f/config
{:id-attrs #{:kr.domain/uuid :system/uuid :view/uuid}
:dispatch [:some-other-config-event]
:trace-queue-size 100}])
The currently available options are:
-
:id-attrs
: [optional] A set of unique id attributes that configure the graph db. A default set is provided byreflet.db/default-unique-attributes
. If the debugger is enabled, this default set will always be included to support the debugger implementation, in addition to any you define using this option. -
:dispatch
: [optional] Dispatches an asynchronous event immediately after configuration -
:pull-fn
: [optional] A function that replaces the default pull implementation. This can be a bit involved, in that you need to take the time to understand the input and output contract ofreflet.db/default-pull-impl
, as explained in that function's doc string. But as long as you fulfill this contract you can swap out the entire query parsing engine, and everything about Reflet should continue to work: from the graph db, to the event-sourced, reactive pull queries, to the FSMs, and even the debugger. For example, you could drop in a query engine for GraphQL, or modify the query DSL already implemented by Reflet. -
:trace-queue-size
: [optional] Sets the number of traces captured by the debugger panels, default is 50 -
:debug-hotkey
: [optional] Sets the debugger hotkey character, which when combined with Ctrl, toggles the purple debuggerwith-ref
marks. The default is\j
, which means you would pressCtrl + J
to toggle the debugger overlay.
If you need, you can also define your own configuration handler in
place of ::f/config
, however at minimum it must configure the db
with a set of unique attributes:
(require '[reflet.core :as f]
'[reflet.db :as db])
(f/reg-event-db ::my-config
;; Configured with a custom set of unique attributes
(fn [db _]
(db/new-db db #{:kr.domain/uuid :system/uuid :view/uuid :js/uuid}))
This is not specific to Reflet, but depending on the version of Re-frame you are using, you might see these SLF4J warnings.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
This is due to Re-frame's transient dependency on
org.clojure/tools.logging
. They are harmless, but if you'd like to
suppress them all you have to do is explicitly include a SLF4J binding
in your dependencies, for example:
{org.slf4j/slf4j-nop {:mvn/version "2.0.6"}}
Reflet's polymorphic descriptions should work with the minimum
config. However, if you dispatch an additional configuration event,
:reflet.core/config-desc
, you get some extra features:
- Polymorphic type hierarchies
- Prefers tables
- Customizable type attributes (if you're using the descriptions
feature you'll almost certainly want to override Reflet's default
:kr/type
type attribute) - Removal of stale definitions when hot-reloading code
See the sections on Configuration and Hot-Reloading in the Polymorphic Descriptions document for more details on where and how to dispatch this event.
The example client extends the code seen above to generate two builds:
- Development build: contains Reflet debugger and Re-frame-10x
- Production build: no debugger or Re-frame-10x
The relevant files would be:
The debugger is not included in the application target by default. To use it, there are two steps:
-
Include the
reflet.debug.preload
namespace in your development build configuration. -
Call the debugger render method to mount it to the DOM.
If you're using shadow-cljs
you can load the preload namespace just like Reflet's example
shadow-cljs.edn
:
{:builds {:dev {...
:devtools {...
:preloads [reflet.debug.preload]} ; <- here!
...}}}
If you're using lein-cljsbuild, you might do something like:
{:profiles
{:dev
{:cljsbuild
{:builds
[{:id "dev"
:source-paths ["src" "dev"]
:compiler {...
:preloads [reflet.debug.preload]
:main "my.client"}}]}}}}
Or with figwheel-main:
{:main "my.client"
:preloads [reflet.debug.preload]
...**
Ultimately, each build framework has its own way of including preload namespaces. Read their respective documentations to learn how.
Besides the preload, you also have to mount the debugger onto the DOM.
Important: this needs to happen after any call to
(re-frame.core/clear-subscription-cache!)
.
So the hot-reloadable setup above could be modified like so:
(ns my.client
(:require [my.client.ui :as ui]
[re-frame.core :as f*]
[reagent.dom :as dom]
[reflet.core :as f]
[reflet.debug.ui :as debug]))
(defn render!
"Called on each hot reload."
[]
(f*/clear-subscription-cache!)
(debug/render!) ; Call after sub cache is cleared
(some->> "container"
(.getElementById js/document)
(dom/render [ui/app])))
(defn init!
"Called only once on application start."
[]
(f/disp-sync [::f/config]) ; <- config only happens once, must be synchronous!
(render!)) ; <- then the application runs
Like Re-frame itself, Reflet considers React a peer dependency, which means you need to make sure it is available. If you are already using Re-frame in your project you've probably already done this.
Otherwise, there are generally two approaches.
Either via npm
and
shadow-cljs:
npm install react@17.0.2 react-dom@17.0.2
Or by adding CLJSJS React packages to your project:
[cljsjs/react "17.0.2-0"]
[cljsjs/react-dom "17.0.2-0"]
Everything about Reflet is compatible with React 18. Unrelated to
Reflet, there are a couple of upstream
workarounds
that you may need to use to get the new createRoot
approach to work
with hot reloading. You can include the following revision of Reflet
in your deps.edn
to try it out:
{:deps {io.zalky/reflet {:git/url "https://github.com/zalky/reflet.git"
:git/sha "b9298ec636b523f117ee127e842acb23f6a0bdbc"}}
Note that the dev
build might print some warnings that
reagent.dom/dom-node is deprecated
when compiling the CLJS target,
and an also a warning that ReactDOM.render is no longer supported in React 18.
in the browser console. This is from the re-frame-10x
debugger. If you remove it from the build, everything should work
without any warnings.
Next: References and Application Design
Home: Home