Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Provide a React hook for using ratoms/reactions as part of public API #545

Open
lilactown opened this issue Aug 11, 2021 · 5 comments

Comments

@lilactown
Copy link

lilactown commented Aug 11, 2021

I've been working on a project for some time that integrates our existing reagent/re-frame application with some plain react components developed using helix. In order to do this, I had to do a lot of work to write an integration between reagent's ratoms/reactions and React hooks. I essentially have a custom hook called use-reaction that looks like:

(def db (r/atom {}))

;; somewhere in a React component
(let [db (use-reaction db) ;; subscribe to db and re-render on any changes
        ,,,]
   ,,,)
;; create a reaction that updates anytime `(:counter @db)` changes and re-render anytime it changes
(let [counter (use-reaction
               (react/useMemo
                #(r/reaction (:counter @db))
                #js []))]
   ,,,)

Currently this relies on a lot of internal Reagent machinery in order to properly set up watching and disposing the reaction. I think it would be appropriate to add such an integration point to Reagent proper, so that it can change along with any internal changes.

Would the maintainers be interested in accepting a PR for this?

@lilactown lilactown changed the title Provide a React hook for using ratoms/reactions as part of public API RFC: Provide a React hook for using ratoms/reactions as part of public API Aug 11, 2021
@Deraen
Copy link
Member

Deraen commented Sep 3, 2021

Yes, I'd be interested in including this.

I wonder if there would be some use for the other direction also? Creating a ratom backed by a hook?

@lilactown
Copy link
Author

Thanks. It looks like there's some new development happening in React 18 which I think I would wait for before creating a hook for public consumption: reactwg/react-18#86

The above discussion lays out a plan for React to provide an API to make synchronizing external state (i.e. ratoms) easy to do in concurrent mode.

I wonder if there would be some use for the other direction also? Creating a ratom backed by a hook?

I can't imagine a case right now, and since React component state has different scheduling behavior than ratoms (i.e. they update async, not sync) I would also imagine it would be confusing for users.

@kovasap
Copy link

kovasap commented Feb 13, 2022

I'm in the process of using the react-data-grid DataGrid react component and am trying to get column sorting working in CLJS by following this JS example: https://github.com/adazzle/react-data-grid/blob/b7ad586498ab8a6ed3235ccfd93d3d490b24f4cc/website/demos/CommonFeatures.tsx#L330

My code so far is below, and IIUC, your use-reaction hook looks like it's the key piece I need to make the column sorting logic work. The problem currently for me is that I can update my sorted-rows atom, but the component itself does not update with the new sorted-rows value.

Am I understanding what's you've made correctly? If so, where can I find the code so that I can use it for my case?

(defn maps-to-datagrid
  [maps]
  (let [sorted-rows (r/atom maps)]
    [:div
      [:> DataGrid
       {:columns (clj->js (map datagrid-column (keys (first maps))))
        :defaultColumnOptions #js {:sortable true
                                   :resizable true}
        :sortColumns #js [#js {:columnKey "input" :direction "ASC"}]
        :onSortColumnsChange
        (fn [newSortColumns]
          (let [{columnKey :columnKey
                 direction :direction} (first (js->clj newSortColumns
                                                       :keywordize-keys true))]
            (swap! sorted-rows
                   #(sort (fn [m1 m2]
                            (prn m1 m2)
                            (let [v1 (get columnKey m1)
                                  v2 (get columnKey m2)]
                              (if (= direction "ASC")
                                (< v1 v2)
                                (> v1 v2))))
                          %))))
        :rows (clj->js @sorted-rows)}]
      [:button {:on-click #(csv/download-as-csv maps "data.csv")}
       "Download as CSV"]]))

@lilactown
Copy link
Author

lilactown commented Feb 13, 2022

@kovasap the problem with your code seems unrelated to this issue. I'd suggest opening a separate one, or asking in the #reagent channel in clojurians.net slack team for help.

@Deraen
Copy link
Member

Deraen commented Apr 1, 2022

useSyncExternalStore is now available with React 18.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants