Last active
December 9, 2019 19:01
-
-
Save lilactown/e93a1a0ab25d40df006d77f405c1e535 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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")) |
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
Cool, thanks for the explanation. Another question:
Is there a story on interop where JS components will give you already-in-JS prop maps, which you need to use from CLJS? My current solution is to wrap them in
bean
so that they can be passed to a anhx
component, perhaps doing amerge
with some other props too. Would be helpful if$
recognised plain JS maps and didn't try to convert them. A function for prop merging could also help.An example of how this looks like from a real component, integrating react-beautiful-dnd -- which brings in Draggable: