Help with react rendering in cljs, reagent atoms vs normal atoms


I have a re-frame/reagent front-end SPA that displays tables using the excellent react-table (v6), basically doing a big version of:

All the tables have filters - for ease of use it’s quite nice for users to filter one table and then have all the other tables filtered the same way.

The pseudo code is:

(def table-filter (r/atom []))

(defn covid-react-table
    [:> ReactTable
     {:data                @res ;using the github link above replace with (apply concat (repeat 50 @res)) to observe slow behaviour
      :columns             covid-table-columns
      :defaultFiltered     @table-filter
      :onFilteredChange    #(reset! table-filter %)}])

This works as intended. However as the table grows, typing in the filter box becomes very slow. I think this may be a bug in react-table and related to:

*if I use an atom instead of r/atom, this works very fast, and will re-render correctly as the user navigates from one page to another. BUT - if two tables are rendered on the same page only the table where the user types the filter, filters. Can I trigger a manual re-rendering of all tables as the normal atom filter changes - something like add-watch?
*alternatively can I do the #(reset! table-filter %) a second after the user stops typing? so the table doesn’t “freeze” between each letter
*finally, how would one go implementing the stackoverflow solution in cljs, assuming this is the correct one? I’m not sure where to put that onFechData function and how to refer to its arguments.

Thanks a lot!

It sounds like when you have multiple large tables on the page, and re-render each of them when filtering, you see performance issues. Have you tried profiling your app to see what is slow?

This doesn’t sound like it has to do with the stackoverflow question you posted. There’s nothing to support that :onFetchData is being called twice; it doesn’t look like you use :onFetchData at all in your code.

Thanks for the comment. How would you do the profiling? I’m afraid I have no experience doing that.

To be clear, even with just one table, it gets very slow when I add the onDefaultFiltered function (that is the culprit, much like the stackoverflow link). It is my impression that onFetchData happens behind the scenes and the suggested solution is to overload it.

My guess of what’s going on is that react-table filters the table on user input (everytime I press a key), but then notices that the defaultTableFilter has changed (as reagent notices the component depends on the atom that just changed), then tries to render the table once again - so two filters and renderings everytime I press a key instead of just one.

onFetchData is only called if you pass it in. It is necessary if you want to send requests to some backend. According to your code, you are not using it. I’m basing this on the docs here: GitHub - tannerlinsley/react-table at v6

You can use your browser’s developer tools to do general profiling of your application. React’s browser extension also has a profiler that can be useful for narrowing down component render bottlenecks. I would use both of these to figure out what your app is doing.

Based on my experience with Reagent, I have a hunch that it could be in part due to the fact that using :> in Reagent to render React components does a deep conversion of whatever props you pass to JS objects. This deep conversion on every key press of large amounts of data could be contributing a lot to what’s causing the render to be slow.

However, this is just a hunch. Profiling your application is the best way to determine what the cause is.

Right - thanks for pointing me in the right direction, you are right it’s the conversion of the table that takes time. I’m getting a noticeable improvement if I keep the raw data (@res in my example) as a js object - not quite as fast as using a normal atom (I guess it’s still re-rendering at every keystroke), but at least I can share the filter across tables in the same page.

Thank you!

1 Like