Code Monkey home page Code Monkey logo

fulcro-troubleshooting's Introduction

fulcro-troubleshooting

A development-time library for Fulcro that helps to detect problems earlier and find and fix their root cause faster.

For additional help, see the Fulcro Troubleshooting Decision Tree.

Table of content:

Rationale

Fulcro does an awesome job of checking your code and providing helpful messages but it is inherently limited by the fact that most of its checks are compile-time. fulcro-troubleshooting checks your application at run time and thus has much more insight into what is really going on. It also integrates with the UI so that you see the errors and warnings right in the UI, next to the place where you observe the problem.

What can it do?

Check: Proper query inclusion

Warn you when a component's query is not included in its parent's:

demo missing join

Experimental configuration (subject to change):

(set! holyjak.fulcro-troubleshooting/*config*
      ;; return truthy to check the inclusion of the component's query in an ancestor
      {:query-inclusion-filter (fn [component-instance comp-class] 
                                 (not= comp-class :com.example.ui/MyComponent))})
;; OR: (when goog.DEBUG (set! js/holyjak.fulcro_troubleshooting._STAR_config_STAR_ {..}))

Check: Valid idents

Warn when there is something fishy about the component's ident:

demo bad ident - map demo bad ident - nil

Check: Presence of child data

Warn when there is no data for a child, perhaps because the data has failed to load, or is at the wrong place of the DB, or because you have not provided :initial-state for the component (which is optional but crucial e.g. for Link Query - only components):

demo missing join data

Experimental configuration (subject to change):

(set! holyjak.fulcro-troubleshooting/*config*
      ;; return truthy for any join prop that should be checked for having non-nil data in the props:
      {:join-prop-filter (fn [component-instance prop] (not= prop :jh/address))})
;; OR: (when goog.DEBUG (set! js/holyjak.fulcro_troubleshooting._STAR_config_STAR_ {..}))

You can also get rid of this warning by using :initial-state and setting it to something non-nil such as [] for a list or {} for a map. (Though remember that in the Template Form {} means "include initial state from the child" so, if there is a child element for that prop, set also its initial state. And remember to propagate the initial state up all the way to the root component.)

Check: Valid :initial-state

Ideally, you would use the template form of :initial-state as it checks that you only include props that you query for.

This check controls that (a) you actually return either nil or a map and (b) that the map has no key you do not query for (contrary to the template form check, this works also for the lambda form, though it is less powerful).

For example, given the following component:

(defsc Character [_ _]
  {:query [:character/fname :character/surname :character/age]
   :ident :character/id
   :initial-state (fn [p] {:character/id (:id p)
                           :character/fname "Jon"
                           :ui/alive? true
                           :character/surname "Snow"
                           :character/sex :m
                           :character/age 18})}
  ...))

we'd get a warning like this one:

demo lambda initial state extras

Experimental configuration (subject to change):

(set! holyjak.fulcro-troubleshooting/*config*
      ;; return truthy for any initial state key that should be checked for having being in the query:
      {:initial-state-filter (fn [component-instance prop] (not= prop :jh/address))})
;; OR: (when goog.DEBUG (set! js/holyjak.fulcro_troubleshooting._STAR_config_STAR_ {..}))

Check: No duplicates in a query

Warn when the same property is used repeatedly in a query (ignoring the content of sub-queries). Duplicates don't make sense, since a property can only be included once in the props map anyway. It can cause unexpected behavior, in the least all but one of the occurrences being ignored.

Example:

(defsc User [_ props]
  {:query [{:user/location (comp/get-query Location)} :user/name #_... :user/location]
   :ident (fn [] [:component/id :User])}
   ...)

demo duplicates in a query

User components get wrapped with React Error Boundary

Non-Fulcro components are wrapped with an Error Boundary so that if their render throws an exception, it is caught and displayed in the UI, instead of taking the whole page down.

Status

Mature. Bugs are fixed, pull requests reviewed, and feature requests considered.

Get in touch with @holyjak in the #fulcro channel of the Clojurians Slack if you have any questions, problems, ideas, or comments.

Usage

You need to do three things:

(1) Add the library to your project:

;; deps.edn
:aliases
{:dev {:extra-deps {holyjak/fulcro-troubleshooting
                    {:git/url "https://github.com/holyjak/fulcro-troubleshooting"
                     ;; BEWARE: run `clojure -X:deps git-resolve-tags` to insert 
                     ;; the correct :sha (warning: it will reformat the file :'( )
                     :sha "b3648778862b5f0cb792134f459092e59ec2d663" :tag "latest"
                     }}}

TIP: Use Fulcro ≧ 3.7.1 (3.7.1 prevents some false positives).

(2) Make sure that the :dev alias is activated and the library's namespace is automatically required:

;; shadow-cljs.edn
{:deps {:aliases [:dev]} ; <-- this
 :builds {:main {:devtools {:preloads [holyjak.fulcro-troubleshooting ...] ...} ; <-- and this
                 ...}}}

(3) When you create your Fulcro/RAD app, add the middleware provided by the library:

(ns my.app
  (:require
    ;; [holyjak.fulcro-troubleshooting] ; add if you haven't added it as :preload
    [com.fulcrologic.fulcro.application :as app]))

(defonce app (app/fulcro-app {:render-middleware 
                              (when goog.DEBUG js/holyjak.fulcro_troubleshooting.troubleshooting_render_middleware)}))
;; we use js/.. instead of holyjak.fulcro-troubleshooting/troubleshooting-render-middleware so that
;; the code will still compile for prod release, when the lib is not included

Limitations

fulcro-rad-demo

We have some false-positives when applied to fulcro-rad-demo, as of 2024-01:

  • /account-master-detail - warns about nil join prop :ui/account until an account is selected from the list. (Not sure I can do anything about it, perhaps other than not checking :ui props?)

Feature ideas

  • check initial state (if present) to be a map with keys <= query keys

License

Copyleft 2021 Jakub Holý

Distributed under the Unlicense, see http://unlicense.org/.

fulcro-troubleshooting's People

Contributors

aeberts avatar holyjak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Forkers

aeberts pdenno

fulcro-troubleshooting's Issues

New validation: duplicate items in query

Checking the query for duplicate items. Just had a production problem where the dev had added the same prop to a query twice, and it crashed the resolver (asked pull for a dupe, which it doesn’t tolerate).

Link, ident queries should not require the key to be in initial state

This defsc:

  {:query [{[:all-series '_] (comp/get-query ExampleChart)}]
   :initial-state {:all-series {}}}

will cause these warnings

These ident/link query join props [:all-series _] have no data in the client DB [..] you might have forgotten to provide :initial-state {}

Initial state should only contain keys for the props the component queries for. Has these: :all-series.

The check should understand that [:all-series '_] = :all-series (and similarly for ident queries)

fulcro-troubleshooting breaks fulcro-inspect when goog.DEBUG set to `false`

Hey Jakub!

fulcro-troubleshooting appear to break fulcro-inspect and the element dom picker when using fulcro-troubleshooting with goog.DEBUG set to false in shadow-cljs.edn.

When goog.DEBUG is set to false, fulcro-inspect shows the no app connected message.

To explore the issue on your machine, clone the following repo and try running the fulcro-inspect extension:

https://github.com/aeberts/minimalist-fulcro-template-backendless

Happy to help debug if you're not able to reproduce on your end.

all the best,
Alex

Query checking does not detect closest stateful ancestor

The warning for queries not being joined to a stateful ancestor does not appear to check it's nearest ancestors and issues a spurious warning for the following code:

https://github.com/aeberts/fulcro-template/blob/fc81a18d8ec55efac29fb89c9abb6649d4825360/src/main/app/ui/root.cljs

Warning text for reference: "The query of app.ui.root/TodoItem should be joined into the one of its stateful ancestor app.ui.root/Root (nil). Something like: {:some-prop (comp/get-query app.ui.root/TodoItem)}.

Improve warning about init. state being out of sync w/ query

=> show a diff of the props between int. state and query. Current ex.:

WARNING(s) for com.example.ui/Root (nil):

Initial state should only contain keys for the props the component queries for. Has these: :selected-list, :task-filters, :today, :tags, :projects, :calendar.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.