Fork me on GitHub
#helix
<
2021-02-09
>
wilkerlucio01:02:46

is there a way to use componentDidCatch with Helix? (aka error boundaries: https://reactjs.org/docs/error-boundaries.html)

lilactown17:02:54

there is, you can use defcomponent which defines a class component that you can implement componentDidCatch and other lifecycle methods

lilactown17:02:00

it's very under-documented tho

Derek16:02:18

One of the only things I miss about reagent/re-frame when using helix is the ability to arbitrarily dispatch an event from the REPL? Has anyone solved this in a novel way? I feel like this is a limitation of React’s useContext mostly.

wilkerlucio16:02:29

this is open and up to you to implement, there is the reducer hook, which takes a similar approach

Derek16:02:00

The issue I was finding was that the hooks are only valid to call within a component

Derek16:02:15

Whereas in re-frame, I can just (rf/dispatch … ) anywhere

wilkerlucio17:02:42

you can pass something to your root component

wilkerlucio17:02:14

here is an example of a mini-reframe in Helix:

wilkerlucio17:02:16

(defonce dispatcher* (atom nil))
(defonce app-state* (atom {::counter 0}))

(defmulti dispatcher (fn [action data state] action))

(defmethod dispatcher ::count-up [_ data state]
  (update state ::counter #(inc (or % 0))))

(defn dispatch [state [action data :as foo]]
  (reset! app-state* (dispatcher action data state)))

(defn repl-dispatch [cmd]
  (@dispatcher* cmd))

(h/defnc MiniReframe []
  (let [[state dispatch] (hooks/use-reducer dispatch @app-state*)]
    (hooks/use-effect [dispatch] (reset! dispatcher* dispatch))
    (dom/div
      (dom/button {:on-click #(dispatch [::count-up])} (::counter state)))))

(comment
  (repl-dispatch [::count-up]))

Derek17:02:42

Very cool. Thanks!

lilactown21:02:47

one thing I've been doing some spending some hammock time on is a way to interact with React's low level stuff from a REPL

👍 6
wilkerlucio21:02:52

today I had an idea for a new state management system, something to combine hooks convenience and Fulcro robustness, going to try a POC on it later, this could address some of the issue of REPL interaction too

👀 6
lilactown21:02:24

e.g. you can use the devtool hooks to get a reference to the currently rendered root fiber and traverse it to find components, inspect props and state, even get references to state setters & reducer dispatch fns which you can call to trigger renders

👀 3
oconn21:02:13

> even get references to state setters & reducer dispatch fns which you can call to trigger renders Oh this could be a really cool way to interact with an app. I’ve been getting around this by doing something similar to what @U066U8JQJ shared above.

lilactown22:02:28

yeah the hooks are all there, the interface is what I'm still noodling on

wilkerlucio22:02:20

sneak peak of my POC, showing a bit what user code may look like:

(h/defnc IKey [{:keys [attr]}]
  (let [state (use-root-state app [attr] {})]
    (dom/div "Render: " (pr-str (get state attr)))))

(h/defnc User [{:keys [user-id]}]
  (let [{:user/keys [id name] :as props}
        (use-entity-state app [:user/id user-id]
          [:user/id :user/name]
          {::load true})]
    (dom/div
      (dom/button {:on-click #(update-state props (fn [x] (assoc x :user/id 1 :user/name "bla")))} "Set data")
      (dom/div "User")
      (dom/div "ID: " id)
      (dom/div "Name: " name))))

(h/defnc MyStateManager []
  (dom/div
    (h/$ IKey {:attr :foo})
    (h/$ User {:user-id 123})
    (h/$ User {:user-id 123})
    (dom/button {:on-click #(update-state app (fn [x] (assoc x :foo "bar")))}
      "Change text")))

wilkerlucio22:02:11

the idea is to use hooks to plug some components in a "application" managed upstream (currently passing by hand, but it can go in a context to avoid repetition and make a tree use the same consistent app)

wilkerlucio22:02:39

then you can hook on some root part, or in some entity (kind like Fulcro does)

wilkerlucio22:02:58

the hook includes a query, and notifications are saved to fire refresh on things on state updates

wilkerlucio22:02:18

what I like about it is that is veery opt-in, you can add this just for the parts of application that you want shared data

wilkerlucio22:02:34

and can freely combine with other sorts of local state, like the state hook

wilkerlucio23:02:04

options like ::load true can make the component automatic trigger a remote load for that data, otherwise it just tries to denormalize it from the local db

wilkerlucio23:02:14

I think there can be a lot of controls to decide how load should behave

lilactown23:02:59

looks really nice. this is the kind of use case that I built https://github.com/lilactown/autonormal for

wilkerlucio01:02:08

awesome, I’ll give that a try!