nextjournal / clerk

⚡️ Moldable Live Programming for Clojure
https://clerk.vision
ISC License
1.74k stars 76 forks source link

clerk/vl doesn't update reactively #577

Closed sritchie closed 7 months ago

sritchie commented 7 months ago

This notebook produces quite a bit of flickering. I've added a recursive fibonacci to create an artificial delay, but in https://probcomp.github.io/gen-finance/ the delay is real:

(ns finance.check
  (:require [nextjournal.clerk.viewer :as clerk]))

(clerk/with-viewer
  {:render-fn
   '(fn [_]
      (reagent.core/with-let [!trials (reagent.core/atom 10)]
        [:div
         [:input {:type :range
                  :value @!trials
                  :on-change
                  #(swap! !trials (constantly (int (.. % -target -value))))}]
         [nextjournal.clerk/inspect
          (nextjournal.clerk/vl
           {:schema "https://vega.github.io/schema/vega-lite/v5.json"
            :embed/opts {:actions false}
            :width 650 :height 300
            :data (letfn [(fib [n]
                            (if (or (= n 0) (= n 1))
                              n
                              (+ (fib (- n 1)) (fib (- n 2)))))]
                    {:values (for [x (range @!trials)]
                               (do (fib 16)
                                   {:x (rand)
                                    :y (rand)}))})
            :layer
            [{:mark :point
              :encoding {:x {:field :x :type "quantitative"}
                         :y {:field :y :type "quantitative"}}}]})]]))}
  {})

Here's the effect: 2023-11-21 11 32 00

mk commented 7 months ago

Since this is all just in a single :render-fn here's a version that doesn't use the render/inspect indirection but uses the vega-lite viewer's render-fn, nextjournal.clerk.render/render-vega-lite directly. This updates without flickering.

I'd recommend this in general since the other parts of Clerk's viewer API like clerk/mark-presented aren't needed when rendering happens in the same runtime.

(clerk/with-viewer
  {:render-fn
   '(fn [_]
      (reagent.core/with-let [!trials (reagent.core/atom 10)]
        [:div
         [:input {:type :range
                  :value @!trials
                  :on-change
                  #(swap! !trials (constantly (int (.. % -target -value))))}]
         [nextjournal.clerk.render/render-vega-lite
          {:schema "https://vega.github.io/schema/vega-lite/v5.json"
           :embed/opts {:actions false}
           :width 650 :height 300
           :data (letfn [(fib [n]
                           (if (or (= n 0) (= n 1))
                             n
                             (+ (fib (- n 1)) (fib (- n 2)))))]
                   {:values (for [x (range @!trials)]
                              (do (fib 16)
                                  {:x (rand)
                                   :y (rand)}))})
           :layer
           [{:mark :point
             :encoding {:x {:field :x :type "quantitative"}
                        :y {:field :y :type "quantitative"}}}]}]]))}
  {})

Let me know if using the render-fn directly like this is feasible in your use case. If not, we might want to investigate if we can make inspect work across reactive boundaries like that but for the moment I'd recommend not going through inspect.