Fork me on GitHub
#re-frame
<
2022-05-25
>
hadils17:05:59

Hi! I am trying to use https://github.com/ingesolvoll/re-statecharts in a non-UI context. Is this possible?

hadils17:05:31

The answer is no. The macro uses a subscribe which must be called in a reactive context.

j abns18:05:49

re-statecharts wraps https://github.com/lucywang000/clj-statecharts that u can use in non ui contexts.

hadils18:05:18

Thanks.

👍 1
j abns18:05:11

there is also a #statecharts channel for everything statecharts.

hadils18:05:25

Thanks a lot!

Nundrum22:05:19

America, I am confusion. I'm trying to integrate a D3 force graph into a Re-frame app. I'm looking at the example linked below. In my app, it renders in an initial state, but doesn't animate. It's like .tick from the d3/forceSimulation isn't getting called. Anyway, I can't quite get how to make this work. Should I go about this an entirely different way? I was hoping to avoid writing a digraph layout algorithm. https://rollacaster.github.io/hiccup-d3/

p-himik22:05:13

Impossible to tell without the code. But there's probably a solution.

👍 1
Nundrum22:05:59

It's almost a cut-and-paste of what's on the rollacaster site, stuffed into a view:

(defn graph []                                                              
  [:<>                                                                      
   [:div {:id "graph"}                                                      
    [:p {:id "graphdata"} (str "graph data:" @(subscribe [:graph])) ]       
    (let [size 400]
         (-> (d3/forceSimulation (.-nodes data))                            
           (.force "link"                                                   
            (-> (d3/forceLink (.-links data))                               
                (.id (fn [d] (.-id d)))))                                   
           (.force "charge" (d3/forceManyBody))                             
           (.force "center" (d3/forceCenter (/ size 2) (/ size 2)))         
           .stop 
           (.tick 1500))
   [:svg {:viewBox (str "0 0 " size " " size)}                              
    [:g (map (fn [node]                                                     
       [:circle                                                             
        {:key (.-id node), :cx (.-x node), :cy (.-y node), :r 5}])          
        (.-nodes data))]
    [:g
      (map (fn [link]
       [:line {:key (.-index link), 
      :x1 (.-x (.-source link)),                                            
      :y1 (.-y (.-source link)),                                            
      :x2 (.-x (.-target link)),                                            
      :y2 (.-y (.-target link)),                                            
      :stroke "black"}])                                                    
        (.-links data))]])                                                  
   
   ]
  ])                                                                        

steveb8n23:05:10

I have found what I think is a better way to use D3. Wrap some interop around this lib https://airbnb.io/visx/ and then you can forget about all the lifecycle complications.

steveb8n23:05:12

obviously not as nice as hiccup but, since you are doing interop anyway, this reduces a lot of complexity

p-himik07:05:35

@U02UHTG2YH5 The original example is static, the component is never re-rendered. But in your case, you're derefing a sub there - its change will result in a re-render, which the code doesn't handle. So as Steve says above, you gotta deal with proper lifecycle methods to fix that. Either by yourself or by using some library. Take a look here for some examples, including a Reagent wrapper for D3: https://github.com/Day8/re-frame/blob/master/docs/Using-Stateful-JS-Components.md#d3-examples Also note that some examples migth use getElementById - that's suboptimal, you should rely on React refs instead.

Nundrum14:05:04

Thanks @U0510KXTU I will check that out!

Nundrum14:05:15

@U2FRKM4TW I'm halfway getting it. I looked at those very D3 examples you linked, but the forceSimulation seems very different in nature because it kicks off some sort of async processing for updating the node locations. I couldn't figure out how to map that knowledge over to fit this use case. I understand the view is only updating because the subscription. But I don't understand where else to put the setup for the simulation.

p-himik15:05:33

The simplest way to achieve it would be to: 1. Split your view into two - one that accepts all its data via arguments and the other a wrapper for the former that would set up all ratoms/subscriptions and feed the actual values to the wrapped component 2. Make the wrapped component a form-3 one and handle argument changes correctly. No clue what it means it terms of D3 - you'd have to read its documentation to figure out how to substitute its data when the graph has already been set

👆 1
Nundrum15:05:02

Form-3? That's a new thing to me. Looks like that is diving deep into React 😰.

p-himik15:05:33

*Reagent, it's well documented.

Nundrum16:05:13

Ah I see. Reading some of Eric Normand's stuff as I get the chance.

p-himik16:05:56

I'd start with Reagent documentation. ;) Especially this page: https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md

👍 1
gratitude 1
Nundrum16:05:09

Maybe you can clarify something. What counts as a "component?" From the GUI world I have a distinct understanding of them, but that understanding is failing here because a) I don't know JavaScript and b) it's not exactly clear what a component is in Reagent or Re-Frame. Is it just a function that returns Hiccup? A node on the DOM?

Nundrum16:05:23

oh hah it's like you read my mind

sansarip17:05:36

If you’re already on the path to a solution and this comment complicates things further, feel free to ignore it! But, as an alternative to form-3 components, if you’re already familiar with https://reactjs.org/docs/hooks-reference.html, you should be able to do the same thing in a form-1-like component with hooks inside of it.

(defn my-component []
  (let [[someState setSomeState] (react/useState nil)]
    ;; The below effect is similar to the :component-did-mount lifecycle callback of a form-3 component
    (react/useEffect 
      (fn initialize-state []
         (setSomeState "new state")
         (return-cleanup-function))
      [])
    [:hiccup-stuff]))

;; Usage 👇 the :f> is essential because of the hook usage
[:f> my-component]
https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#hooks

Nundrum18:05:35

@U022T96EFV3 Sadly I'm not familiar at all with React, nor am I on a path to a solution. D3 is bewildering to me outside of very cut-and-paste simple examples. And the force directed graph seems like one of the most complex-to-use visualizations it supports.

dvingo18:05:16

You may also want to start with react in js and use redux - https://react-redux.js.org/ Redux is re-frame in many ways. (if you use https://redux-toolkit.js.org/ the toolkit, it includes immutable data structures by default too) because all these cljs libs use react having a solid understanding of it will help in general

Nundrum18:05:32

That completely drops me out of Clojurescript, though. I was hoping to experience the ineffable joy of Clojure on both front and back ends 😄

sansarip18:05:04

:thinking_face: Maybe find a d3 react component library that already exists which you can then use from reagent! Or, find a tutorial that walks you through creating a d3 react component and then convert the JS to CLJS as you go -could be a good/less-daunting learning experience as well. I’m not familiar with it, but the https://airbnb.io/visx/ library linked in a comment above may be what you’re looking for?

dvingo19:05:27

ClojureScript doesn't churn like JS - when you're ready it will be there 😛

1
Nundrum20:05:11

@U0510KXTU @U022T96EFV3 AFAICT, Visx does not have a graph layout algorithm built in. https://airbnb.io/visx/docs/network

alpox09:05:00

When integrating D3 with React/Vue I found it to be most streight-forward to have React only draw an svg and stay in D3-Land when it comes to drawing the contents of the svg. I'd usually have a draw method that does everything D3-related that would update/redraw the svg only through D3. And I'd call that method from lifecycle events (and window resize events etc.) whenever necessary. (Not to forget the unmount to cleanup in case of lingering eventhandlers)

Nundrum23:05:57

@U6JS7B99S I haven't been able to understand how d3-force works well enough to implement any of the examples in Re-frame. I haven't worked in React so I don't know how that approach would map over to Re-frame.

steveb8n23:05:57

fwiw you might be able to learn from this project https://github.com/stevebuik/Stu It’s not re-frame but it’s a good example of interop and transitions in cljs

Nundrum01:05:10

I think my problem is that d3-force is unlike the other d3 visualizations. I've been puzzling over how to use it for a week or more. But there doesn't seem to be any alternative graph layout component to use, so I guess I'm stuck with it.

dvingo15:05:15

there's this lib, not sure if it fits your needs though https://github.com/erikbrinkman/d3-dag

Nundrum16:05:09

Maybe you can clarify something. What counts as a "component?" From the GUI world I have a distinct understanding of them, but that understanding is failing here because a) I don't know JavaScript and b) it's not exactly clear what a component is in Reagent or Re-Frame. Is it just a function that returns Hiccup? A node on the DOM?