Skip to content

Instantly share code, notes, and snippets.

@lilactown
Last active December 9, 2019 19:01
Show Gist options
  • Save lilactown/e93a1a0ab25d40df006d77f405c1e535 to your computer and use it in GitHub Desktop.
Save lilactown/e93a1a0ab25d40df006d77f405c1e535 to your computer and use it in GitHub Desktop.
(ns helix-example
(:require
[helix.core :refer [defnc $ <>]]
[helix.hooks :as hooks]
[helix.dom :as d]
["react-dom" :as rdom]))
(defnc Greeting
"A component which greets a user. The user can double click on their name to edit it."
[{:keys [name on-name-change]}]
(let [[editing? set-editing?] (hooks/use-state false)
input-ref (hooks/use-ref nil)
focus-input #(when-let [current (.-current input-ref)]
(.focus current))]
(hooks/use-layout-effect
:auto-deps ;; automatically infer deps array from body; stand in for `[editing?]`
(when editing?
(focus-input)))
(d/div
"Hello, " (if editing?
(d/input {:ref input-ref
:on-change #(on-name-change (.. % -target -value))
:value name
:on-blur #(set-editing? false)})
(d/strong {:on-double-click #(set-editing? true)} name)
"!")))
(defnc App []
(let [[state set-state] (hooks/use-state {:name "Helix User"})
;; annotate with `:callback` metadata to automatically wrap in
;; `use-callback` and infer dependencies from local context
on-name-change ^:callback #(set-name assoc :name %)
;; annotate with `:memo` metadata to wrap in `use-memo` and infer deps as well
name ^:memo (:name state)]
(<> (d/h1 "Welcome!")
($ Greeting {:name name}))))
(rdom/render ($ App) (js/document.getElementById "app"))
@lilactown
Copy link
Author

lilactown commented Nov 26, 2019

Dynamic props are tough.

What I've done so far is introduce an idea of spread props in the $ macro (this includes helix.dom macros too). So you can do something like this:

(let [props {:on-click #(js/alert "clicked!")}]
  ($ MyComponent {:style {:color "red"} & props}))

This way, props are always written as a literal map and you can opt-in to dynamically setting props when needed. This also handles merging; props passed in via the & key will be merged into the JS object generated by the literal props and override them.

So the way that you would handle your case would be like:

(defnc DraggableSelectableRow [{:keys [id index value cells-component]}]
  (let [selected? (is-selected? id)]
    ($ Draggable {:draggableId id
                  :key id
                  :index index}
      (fn [provided snapshot]
        (d/tr {:ref (.-innerRef provided)
               :class [(when selected? "table--selected")
                       (when (.-isDragging snapshot) "table-row-dragging")]
               & (bean (.-draggableProps provided))}
           ($ table/SelectionCell {:id id :is-selected selected?})
           ($ cells-component {:value value})
           (d/td {:class "table-row--draggable" & (bean (.-dragHandleProps provided))}
              (d/span {:class "icon move"} (d/i)))))))

Some additional logic could be added to check if spread props are a map? and if not, treat it like a JS object and merge it. That would remove the need for the bean wrappers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment