This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-07-30
Channels
- # announcements (6)
- # bangalore-clj (1)
- # beginners (169)
- # boot (8)
- # calva (20)
- # cider (32)
- # clj-kondo (78)
- # cljdoc (42)
- # cljs-dev (4)
- # clojure (126)
- # clojure-china (35)
- # clojure-dev (5)
- # clojure-europe (3)
- # clojure-italy (40)
- # clojure-nl (15)
- # clojure-portugal (1)
- # clojure-spec (4)
- # clojure-uk (67)
- # clojurescript (89)
- # community-development (2)
- # core-async (34)
- # cursive (46)
- # datomic (14)
- # dirac (1)
- # duct (8)
- # emacs (11)
- # events (5)
- # fulcro (21)
- # jackdaw (14)
- # jobs-discuss (6)
- # off-topic (37)
- # pedestal (24)
- # perun (1)
- # quil (3)
- # reagent (27)
- # reitit (1)
- # remote-jobs (1)
- # shadow-cljs (47)
- # sql (1)
- # vim (9)
- # yada (1)
I spent a couple hours yesterday debugging an app that’s for serializing arbitrary data and sending it over a wire for introspection. For some reason my EDN decoding was breaking on the other side. I finally realized that a Reagent Reaction’s IPrintWithWriter
isn’t valid EDN:
#<Reaction: ...>
Created an issue here: https://github.com/reagent-project/reagent/issues/439
I'm currently using a combination of re-frame, reagent, and my own component library written in React. I have a React table component that takes a renderRow
prop. renderRow
is a function that follows the React function-as-a-child pattern; the table component passes some internal state and primitive table components to the renderRow
function, which allows my consuming app to have control over rendering of the table rows.
In CLJS land, I'm keeping track of some state via re-frame. Inside of my renderRow
definition, I have code that looks something like the following
:renderRow
(fn [props]
(let [{:keys [isExpanded handleExpandableRowClick row]}
(js->clj props :keywordize-keys true)
user-id (:id row)
roles @(rf/subscribe [::subs/roles-for-user row])
roles (sort-roles-by-app-name roles)
assigned-application-ids (map #(:application_id %) roles)]
(print "Roles: " roles)
(into [:<>] (for [role roles] [:div role]))
Even though the renderRow
function is dereferencing a re-frame subscription, renderRow
does not seem to get called when the data returned by the subscription changes, and as a result nothing in the renderRow
function gets triggered. My guess is that unless props
changes, the React table is not re-rendering and calling renderRow
.
Is there a way to force a re-render there? Or perhaps design my component in a way where I don't have to manually force re-renders?whether or not dereferences cause re-renders is all about how you call the function
Inside of the React table, I'm calling something like
customRenderRow({
ExpandableTableRowLink,
NonExpandableColCell,
isExpanded,
handleExpandableRowClick,
row,
ExpandableTableRow,
})
[:> DataTable
{:expandable true
:background "#FAFAFA"
:displayExpandableColumn false
:renderRow
(fn [props]
(let [{:keys [isExpanded handleExpandableRowClick row]}
(js->clj props :keywordize-keys true)
user-id (:id row)
roles @(rf/subscribe [::subs/roles-for-user row])
roles (sort-roles-by-app-name roles)
assigned-application-ids (map #(:application_id %) roles)
...
the way that reagent works, is that the rerender-when-a-subscription-has-changed-that-I've-dereferenced magic is wired up when your component is parsed in a hiccup form
if you have a component like:
(defn render-row []
(let [roles @(rf/subscribe [::subs/roles-for-user row])]
[:div (pr-str roles)]))
and you call it like:
(render-row)
it will render only once. however, if you render it in a hiccup vector inside of a React element tree, it will reactively re-render:
(r/as-element [render-row])
so what I would do is pull out your render row definition into it's a named function, and then to your DataTable component do something like:
:renderRow (fn [props] (r/as-element [render-row (js->clj props :keywordize-keys true)]))
so the key is to have the subscription inside of the component that's going to be rendered in hiccup form
So I took your suggestion and moved my original renderRow
function into a render-row
reagent component. Now I'm passing the following into renderRow
prop.
:renderRow (fn [props]
(r/as-element [render-row props]))
And inside of the render-row
function, I have something like...
(when isExpanded
[:<>
[render-unassigned-applications
(merge common-props {:app-ids app-ids-to-assign})]
[render-row-footer
(merge common-props
{:handleExpandableRowClick handleExpandableRowClick
:user-id user-id
:roles-to-update roles-to-update
:initial-roles initial-roles
})]])
where render-row-footer
is
(defn render-row-footer
[{:keys [expandable-row handleExpandableRowClick user-id initial-roles]}]
(let [roles-to-update @(rf/subscribe [::subs/roles-to-update.ui {:user-id user-id}])
is-dirty? (not= initial-roles roles-to-update)]
[:> expandable-row {:style {:height "72px"}}
[:td {:colSpan "3"
:style {:padding-left "16px"}}
[:> Button
....
I'm expecting the render-row-footer
to rerender-when-a-subscription-has-changed-that-I've-dereferenced but it still does not seem to be happening