Liking cljdoc? Tell your friends :D

logo

Fulcro is a library for building data-driven full-stack applications for the web. It uses React and is written in Clojure and Clojurescript.

NOTE: If you are using Java 9, you may need to include "--add-modules java.xml.bind" to your JVM.

1. Version Differences

Fulcro has undergone a number of face-lifts over its lifetime.

1.x

An add-on library for Om Next. Has various limitations as a result of this, and several bugs that are deep within Om Next (which was not getting updates).

2.0 - 2.4

No longer depends on Om Next. Public API is the same, except where improvements and fixes were needed.

2.5

Integrated co-located CSS for better support (e.g. localized-dom), made all server dependencies dynamic, rewrite of i18n. This version requires you to change a few i18n things if you were using it, and adds API to the DOM to make UI much more concise.

2.6

Changes for React 16+. Changes in React led to a change in the underlying rendering refresh optimizations.

3.0

This version. A nearly complete rewrite of the internals. Simplifies a lot of things, makes others more extensible, and preserves the shape of the API; however, it is not 100% API compatible with prior versions. In particular in the course of fixing some long-standing issues subtle semantic differences were desirable/unavoidable in the transaction processing system.

2. Trying it Out

The documentation is mostly correct in the Developer’s Guide.

defsc

The new defsc looks like the old one, and even does some of the same error checking (NOTE: there is a known bug in the new props destructuring checks I have not had time to fix…​the workaround is to use lambda query/ident if you run into it). However, defsc’s option map support user-additions. Anything you include in that map now appears (unaltered) in `component-options. This allows library authors to co-locate information on a component without having to modify the macro. The "magic" behaviors of query/ident/initial-state (and error checking) are still present for developer aid and bw compatibility. Some of the arities may be wrong. I was considering making them all lambdas at one point and may have screwed something up. Please report any issues.

Namespaces

All of the namespaces changed:

├── com
│   └── fulcrologic
│       └── fulcro
│           ├── algorithms
│           │   ├── application_helpers.cljc
│           │   ├── data_targeting.cljc       ; targeting for loads/mutations
│           │   ├── denormalize.cljc          ; db->tree
│           │   ├── form_state.cljc           ; from 2.x
│           │   ├── indexing.cljc             ; internal
│           │   ├── merge.cljc                ; merge-component!, etc.
│           │   ├── misc.cljc                 ; 2.x util, roughly
│           │   ├── normalize.cljc            ; tree->db
│           │   ├── scheduling.cljc           ; internal
│           │   ├── tempid.cljc               ; from 2.x
│           │   ├── transit.cljc              ; from 2.x
│           │   ├── tx_processing.cljc        ; internals of new transact!
│           │   └── tx_processing_debug.cljc  ; debugging util for internals of new transact!
│           ├── application.cljc              ; App constructor: fulcro-app
│           ├── components.cljc               ; get-query, get-ident, etc.
│           ├── data_fetch.cljc               ; from 2.x: load, load-data, etc.
│           ├── dom
│           │   ├── events.cljc               ; various helpers from 2.x
│           │   ├── html_entities.cljc
│           │   └── icons.cljc
│           ├── dom.clj                       ; DOM from 2.x
│           ├── dom.cljs
│           ├── dom_common.cljc
│           ├── dom_server.clj                ; DOM for SSR (NOT WORKING YET)
│           ├── inspect                       ; package of nses for talking to Chrome Inspect plugin
│           │   ├── diff.cljc
│           │   ├── preload.cljs              ; Use THIS for using Inspect.  Do NOT include the inspect dependency.
│           │   └── transit.cljs
│           ├── macros
│           │   ├── defmutation.clj           ; macro internals. Do not use.
│           │   └── defsc.clj
│           ├── mutations.cljc                ; client-side defmutation
│           ├── networking
│           │   ├── http_remote.cljs          ; normal client remote (no longer uses protocols)
│           │   ├── mock_server_remote.cljs   ; mock server for use in cljs
│           │   └── server_middleware.clj     ; middleware elements from 2.x for rolling your own server
│           ├── rendering
│           │   ├── ident_optimized_render.cljc  ; Default rendering optimization.  Not perfect yet, but fast.
│           │   └── keyframe_render.cljc         ; More like Fulcro 2.x rendering. Relies on shouldComponentUpdate for performance.
│           ├── routing
│           │   ├── dynamic_routing.cljc      ; Router from Incubator
│           │   └── union_router.cljc         ; Fulcro 2.x routers (old dynamic router untested, possibly broken)
│           ├── specs.cljc
│           └── ui_state_machines.cljc        ; From Incubator

2.1. Creating An Application

Fulcro applications no longer use anything from Om Next. There are no protocols, no reconciler, etc. A Fulcro application is implemented as a map, and that app is usable everywhere the reconciler was in 2.x. There is no need to store it in an atom because it uses atoms internally to deal with state.

For hot code reload, you should use defonce to make your application in some central location that can be used from any other namespace:

(ns my.app
  (:require
    [com.fulcrologic.fulcro.networking.http-remote :as fhr]
    [com.fulcrologic.fulcro.application :as app]
    [com.fulcrologic.fulcro.components :as comp]
    [com.fulcrologic.fulcro.data-fetch :as df]
    [my.app.ui :as ui]
    [taoensso.timbre :as log]))

(defonce app (app/fulcro-app {:remotes   {:remote (fhr/fulcro-http-remote {:url "/api"})}}))

At some point in your logic you will want to associate the root of your UI with the application via app/mount!:

(app/mount! app ui/Root "app")

2.2. Significant Changes

I call these significant more for their long-term implications than their impact on existing code. Most existing code will be trivial to port to Fulcro 3, and should operate without much further change; however, some of the "hard edges" of Fulcro 2 are solved by these changes, and as such they are "significant" in that sense.

2.2.1. Defsc

As mentioned earlier: defsc no longer uses protocols at all. The options map is "beefed up" but the defsc macro, but in fact you can simply create a "contructor function" and call configure-component! on it and pass a non-magic options map to create a component. The macro just helps you with typos and is easier to read.

This also means things like CSS can now be a pure library concern (and in fact, fulcro-garden-css is where that functionality lives now).

Some things that were macros in Fulcro 2.x no longer need to be. The incubator dynamic routers are an example of this. The old union router is still a custom macro because it actually emits more than one thing.

2.2.2. Transact Changes

The most significant change is in transact!, which is now in the component namespace. Transactions are now safe to submit from anywhere in the code base, even from within helpers that are running within swap! against the state atom!

The transact! function just puts the tx on a submission queue. That’s it. At some point in the (very) near future Fulcro will process the current submissions into an active queue.

This simplifies a lot of things:

  • You no longer need ptransact!. Just embed a transact! in the ok-action of your mutation.

  • Timing issues in dynamic routing and ui state machines should be easier to avoid/solve.

  • You can submit transactions without using setTimeout, and be sure they will activate in the order submitted.

2.2.3. Mutation Generalizations

Mutations have become an even more central notion in the library. All versions of Fulcro have actually treated loads internally as mutations, because in fact a load is a combination of some state changes (recording the fact that something is loading) and fetching the actual data.

Prior versions of Fulcro had Om Next structure in the middle. Version 3 does not. The logic in 3 is much more direct:

  • A transaction is written as it always has been

  • Each element of the transaction (mutations) can choose local and remote behaviors

  • Optimistic actions run first

  • Network actions go on a queue and run in order

All of that should sound pretty much identical to what you’ve been doing all along. The big difference is what happens next:

  • Network results are delivered straight to the result-action of the mutation. If the user does not supply a result-action, then the defmutation macro supplies a default that behaves like Fulcro 2.

As a result any full-stack operation is completely under your control:

(defmutation do-thing [params]
  (action [env] ...optimistic actions...)
  (remote [env] true)
  (ok-action [env] ...your custom action type!...)
  (result-action [{:keys [result app handlers] :as env}]
    (let [{:keys [status-code body]} result
          {:keys [ok-action]} handlers]
      (if (= 200 status-code)
        (ok-action env)
        ...))))

This maintains backward compatibility while also giving you the power to implement things like pmutate from incubator without having to resort to magical transaction transforms. The fact that you can trigger new transactions from any part of that code means that chaining behaviors is now trivial and no longer needs the concept of ptransact! (though there is an :optimistic? false option of the new transact! that emulates that behavior.

Interestingly, this also makes it super easy to generalize loads even more than before. Loads are now implemented something like this (simplified for ease of understanding):

(defmutation internal-load! [{:keys [remote query marker] :as params}]
  (action [{:keys [app]}] (set-load-marker! app marker :loading))
  (result-action [{:keys [result app] :as env}]
      (if (load-error? result)
        (load-failed! env params)
        (finish-load! env params))))
  (remote  [{:keys [ast]}] (eql/query->ast query)))

and the data-fecth API just internally calls transact! for you.

The multimethod mutate is still at the center of this; however, the arguments have changed. The multimethod is sent only an env, which contains (→ env :ast :params).

2.3. Using Inspect

Do NOT include Fulcro Inspect as a dependency. Instead, Fulcro now includes the client-side code necessary to talk to the Chrome extension without pulling in all of inspect’s dependencies in your project. Just add the following preload:

 :builds   {:app  {:target     :browser
                   ...
                   :devtools   {:preloads [com.fulcrologic.fulcro.inspect.preload]}}

At the the db, transactions, and network tab are known to mostly work. Expanding Inspect to include some cool new features is one of my top priorities. I want to support better helpers for UI state machines, perhaps some UI performance monitoring, etc.

2.4. Known Issues

  • If you use link/ident joins in UI queries the new optimal render may miss refreshes. Use with-optimized-renderer in application-helpers (see the doc string) to select keyframe rendering if you have problems. -

  • Some Inspect features are not implemented.

  • Inspect transactions report the before/after db as the same every time, even though it is changing (this has to do with changes in how transactions are processed internally…​the inspect hook isn’t hooked up in the right places). -

3. Status

Version 3 is now officially in Alpha. Most APIs have been ported, and some have been tested. Some code (CSS and websockets) was moved to external libraries to reduce complexity and dependencies.

The basic data fetch API has been ported, so most of a full-stack working app can be written (and is in the repo as todomvc). Only the port of HTTP network layer remains before we have something usable for full-stack.

The general road map (with status) is:

  • Rewrite Transaction Internals (100% done, but needs more integration testing)

    • Write tx processing that is extensible, and can support all currently-known use-cases (100%)

    • Make tx system pluggable (100%)

    • Support for new tx-combining at network layer (designed, not implemented)

    • New defmutation (possibly to be renamed) (100%)

      • Support for result-action (100%)

      • Support for "extensible" mutation semantics (100%)

      • Support for quote-free transactions (100%)

  • Network Layer (100%)

    • Write adapters or otherwise build new remote networking

  • Merge Logic (100%)

    • Split merge routines into easily reusable bits (100%)

    • Make it possible for users to easily choose/customize merge strategy (100%)

    • Figure out the right place to put helpers like integrate-ident, etc. (100%)

  • App DB normalization/denormalization (100%)

    • Improve performance of db→tree (100%, up to 6x faster)

    • Factor logic out into clear namespaces (100%)

    • Add better tests (100%)

  • Components

    • Support for React class-based components (100%)

      • Rewrite of defsc (100%)

        • Drop protocols (100%)

        • Support extensibility (90% complete)

    • Turn component-local CSS into a pure library concern (100%)

    • Turn i18n into a pure library concern (easy)

  • Move UI State Machines into this library (90%, needs more testing)

  • Move Dynamic Router into this library (90%, needs more testing)

  • Move to EQL as Source of AST logic (100%)

  • Minimize dependencies (100%)

  • Nice to Haves (depends a bit on contributors)

    • React Hooks-based defsc (designed and prototyped, but needs integration work)

Fulcro is:

Copyright (c) 2017, Fulcrologic, LLC The MIT License (MIT)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Can you improve this documentation?Edit on GitHub

cljdoc is a website building & hosting documentation for Clojure/Script libraries

× close