I keep getting this warning on my FlatList
component (react native), however I know my list element component is pure. Is there any way to tell react that it's a PureComponent
through reagent?
Just as an idea - you can try memoization of the relevant view functions.
Of course, even if it works it would bring a new problem - how to remove items from the cache. FWIW, https://github.com/ptaoussanis/encore has memoization with a TTL and/or max number of entries.
Of perhaps a better way would be to just do what PureComponent
does:
> React.PureComponent is similar to React.Component. The difference between them is that React.Component doesnβt implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.
Reagent components need state for RAtom implementation, so no, PureComponent is not possible. But Reagent components already implement shouldComponentUpdate.
Thanks! Then, do I understand it correctly that using memoization or your own version of shouldComponentUpdate
(if that's even possible) or something like that won't change anything?
Or you could also create "pure" React components and use them inside Reagent components, just remember you can't use Ratoms directly in these components: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#pre-10-workaround
It is possible to use custom shouldComponentUpdate
, Reagent default impl checks the component arguments using Cljs equals method, which works efficiently for Cljs datastructures, JS doesn't have such method normally: https://github.com/reagent-project/reagent/blob/master/src/reagent/impl/component.cljs#L164-L179 but you could e.g. just compare certain fields in your own impl.
Back to the original problem. Not sure how react-virtualized detecs if a component is slow. If the arguments are pure and not changing, could be that all the list items follow some Ratom which is changing? Reactions (or Re-frame subscription) could be used to fix those cases.
E.g.
(defn list-item [foo]
(let [active? (= (:id foo) @active-item)] ...))
is slow, because every list-item is re-rendered if @active-item changes.
(defn list-item [foo]
(let [active? @(rf/subscribe [:active-item? (:id foo)])] ...))
Is fast, because re-render is triggered only if the result from the reaction changes.I am already using a re-frame subscription to get the data which is used to render the list elements. I'm not using ratoms at all
Re-frame subscriptions are ratoms
Maybe "reaction" would be batter name, but I mean using any Reagent reactive data structure or calculation. r/atom
r/reaction
r/track
and rf/subscription
all share the implementation.
And the example holds with subscriptions,
(defn list-item [foo]
(let [active? (= (:id foo) @(rf/subscribe [:active-item-id]))] ...))
Triggers re-render for all items, while the second example wouldn't.Here is the relevant code
js->clj
is slow
Can you also paste ::subs/get-pokemon
and info-card
info-card
on line 99 https://pastebin.com/SAU1rkwx
I am already using as-element
when rendering info-card
Try
(defn render-card [obj idx _separators]
(let [item (.-item obj)]
(r/as-element [info-card (assoc item :index idx)])))
(defn- seperator []
[:> rn/View {:style (:separator styles)}])
(defn- convert-to-array [vec]
(let [arr #js []]
(doseq [x vec]
(.push arr x))
arr))
;; :> converts properties recursively to
;; r/create-element doesn't convert props from clj -> js
(defn card-list []
(r/create-element
rn/FlatList
;; Not sure if convert-to-array is required. Cljs should implement similar methods to Array,
;; but might not be enough for RN.
#js {:data (convert-to-array @(rf/subscribe [::subs/get-pokemon]))
:getItemLayout (fn [_ index]
;; Never use clj->js if you have map literal, #js is MUCH faster.
#js {:length 120
:offset (* 120 index) :index index})
:initialNumToRender 6
:ItemSeparatorComponent (r/reactify-component seperator)
:keyExtractor (fn [item-obj idx] (str (:id item-obj)))
:progressViewOffset true
;; Memoize probably doesn't help here.
:renderItem render-card}))
Important parts being replacing clj->js
with #js
and using r/create-element
instead of :>
Here the :data
won't be recursively converted to JS objects, so you don't need to convert it back to Cljs in render-card
And I checked RN doesn't care if you use PureComponent or anything, it just checks how long the render calls take: https://github.com/facebook/react-native/blob/46be292f671c70aac4ecc178c96e3a2a6a3d16da/Libraries/Lists/VirtualizedList.js#L1564-L1582
oh lol, yeah this seems way snappier. Thanks!