Fork me on GitHub
#reagent
<
2021-01-01
>
skykanin14:01:23

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?

p-himik14:01:26

Just as an idea - you can try memoization of the relevant view functions.

p-himik14:01:36

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.

p-himik14:01:40

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.

juhoteperi14:01:24

Reagent components need state for RAtom implementation, so no, PureComponent is not possible. But Reagent components already implement shouldComponentUpdate.

p-himik14:01:53

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?

juhoteperi14:01:04

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

juhoteperi14:01:53

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.

👍 3
juhoteperi14:01:07

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.

juhoteperi14:01:14

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.

skykanin14:01:34

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

juhoteperi15:01:39

Re-frame subscriptions are ratoms

juhoteperi15:01:46

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.

👍 3
juhoteperi15:01:54

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.

skykanin15:01:54

Here is the relevant code

juhoteperi15:01:51

js->clj is slow

juhoteperi15:01:33

Can you also paste ::subs/get-pokemon

juhoteperi15:01:33

and info-card

skykanin15:01:37

I am already using as-element when rendering info-card

juhoteperi15:01:24

(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}))

juhoteperi15:01:52

Important parts being replacing clj->js with #js and using r/create-element instead of :>

juhoteperi15:01:22

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

👍 3
juhoteperi15:01:59

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

skykanin15:01:28

oh lol, yeah this seems way snappier. Thanks!