reagent

A minimalistic ClojureScript interface to React.js http://reagent-project.github.io/
Alex 2020-04-23T01:20:28.198700Z

Hey guys I'm trying to understand a bit more of reagent. I've got the following component:

(defn c []
  (let [state (r/atom false)]
    (fn []
      (let [edit (fn [] (reset! state true))
            data (filter (fn [v] (if @state (even? v) (odd? v))) [1 2 3 4 5 6])]
        [:div
         (for [item data]
           ^{:key item} [:p item])
         [:button {:on-click edit } "tap me"]]))))
The reference of state is being updated when I keep pressing the button. But the filter function doesn't seem to be updating. Can someone help me further my understanding as to why this is happening?

lilactown 2020-04-23T01:31:39.199200Z

@alexander.890 I think the problem is that you’re dereferencing the state atom inside of a lazy sequence

lilactown 2020-04-23T01:32:34.200100Z

reagent does some magic… basically, when it executes your render function, it captures any reagent.core/atom dereferences and tracks those to call your function again when it changes

lilactown 2020-04-23T01:33:11.200900Z

but because it’s in a lazy sequence, it doesn’t actually get executed when your component function is called. it gets dereferenced sometime later in reagent’s machinery, when the lazy sequence is realized and turned into react elements

lilactown 2020-04-23T01:34:13.202700Z

if it is due to the lazy sequence, you should be able to wrap your (for ,,,) in a doall to force the realization when executing the render function

Alex 2020-04-23T01:34:32.203100Z

Ah okay that makes more sense, when I moved the filter call inside the (for [ item (filter....)] it properly updated state.

Alex 2020-04-23T01:34:46.203600Z

@lilactown thank you 🙂

lilactown 2020-04-23T01:35:22.204200Z

sure thing. I’m not sure why moving the filter inside of the for would make a difference, but to be sure, wrap it in a doall to ensure it always gets realized while executing the render function

👍 1
NoahTheDuke 2020-04-23T14:21:57.207200Z

architecture question: i have a card game where mousing over a card will show the image in the corner of the screen. this is handled by using a channel and a go block that changes the global r/atom app-state, which propagates down to the "card zoom" div. This feels messy, and inhibits my ability to extract the card-view logic into a new file (cuz the main file is nearing 2k lines 😬 )

NoahTheDuke 2020-04-23T14:23:49.208800Z

to maintain the same system, I could create a zoom-channel namespace, move the channel to it, and then require it in both the gameboard namespace and the newly created card-view namespaces, but that feels like i'm missing the point

NoahTheDuke 2020-04-23T14:24:14.209Z

has anyone dealt with something like this?

NoahTheDuke 2020-04-23T14:24:42.209400Z

likewise, as I'm asking questions, is this a good idiom?

(go (while true
      (let [zoom (<! zoom-channel)]
        (swap! app-state assoc :zoom zoom))))

Andrea Russo 2020-04-23T17:18:14.210200Z

is someone still using the https://github.com/ckirkendall/kioo library?

Andrea Russo 2020-04-23T17:18:38.210900Z

It still uses the React.createClass function, which is obsolete

Andrea Russo 2020-04-23T17:18:57.211400Z

I’ve see that now that function has it’s own npm package:

Andrea Russo 2020-04-23T17:19:03.211600Z

https://www.npmjs.com/package/create-react-class

Andrea Russo 2020-04-23T17:19:48.212400Z

but does someone knows how to tell to kioo to use the function in that npm module?

juhoteperi 2020-04-23T17:23:11.214300Z

@andrea.russo Create-react-class in available in cljsjs. Kioo accesses the createClass function through React global object, so you can do something like (:require ["create-react-class" :as createClass]) (set! (.-createClass js/React) createClass) and Kioo should see the function from the package.

Andrea Russo 2020-04-23T17:27:13.214700Z

Tried this approach, but I get this error:

Andrea Russo 2020-04-23T17:27:13.214800Z

failed to load kioo.util.js TypeError: React.createClass is not a function at eval (util.cljc:56) at eval (<anonymous>) at Object.goog.globalEval (app.js:836) at Object.env.evalLoad (app.js:2224) at app.js:2786

Andrea Russo 2020-04-23T17:27:37.215Z

and

Andrea Russo 2020-04-23T17:27:37.215200Z

failed to load sablono.interpreter.js TypeError: React.createClass is not a function

Andrea Russo 2020-04-23T17:28:00.215600Z

I should do the set! before the kioo library is loaded

Andrea Russo 2020-04-23T17:30:37.216Z

I’m using shadow-cljs to import directly npm packages

Andrea Russo 2020-04-23T17:30:49.216300Z

create-react-class is imported through that

lilactown 2020-04-23T17:34:43.216700Z

shadow-cljs has a guide on how to migrate from cljsjs: https://shadow-cljs.github.io/docs/UsersGuide.html#cljsjs

thheller 2020-04-23T17:35:25.217200Z

you can likely just require cljsjs.create-react-class

thheller 2020-04-23T17:35:52.217800Z

looks like something assumes that is available without requiring it itself

thheller 2020-04-23T17:36:18.218100Z

just make sure you require that before loading the kioo sources

Andrea Russo 2020-04-24T09:12:55.233700Z

That solves that problem! Many thanks!

Vitor Barbosa 2020-04-23T18:02:11.220500Z

Hey folks! I have been trying to use reagent with a library for rendering a Markdown Editor called reagent-mde (https://github.com/andrerpena/react-mde). I'm hitting the infamous problem of the cursor positioning inside the`textarea` element that the react-mde library uses internally. I looked into the "fix" for Material UI from https://github.com/reagent-project/reagent/issues/265#issuecomment-397895508 and tried to implement it. I basically forked the project, added a custom props so I could specify which textarea`` component to use, and I'm passing a custom reagent`textarea` component so that the cursor positioning works. This fixed the cursor positioning but left me with another problem. Most of the editor functionalities (transforming text to bold, italic, etc.) depends on a ref to the textarea component. And, of course, when I pass a custom textarea component, the ref points to my component instead of the textarea node itself, and everything breaks because my component does not has the properties the textarea has. I tried making a hack so I could also give a ref for the library, but I could not get it working. Anyone has any idea how I could get it working, implementing a fix for the cursor positioning but still giving the library a ref to the inner textarea component? Here is a git repo with a minimal example: https://github.com/vitorqb/react-mde-and-reagent-example/blob/master/src/reagent_with_mde_example/core.cljs#L9 Just

git clone <https://github.com/vitorqb/react-mde-and-reagent-example.git>
npx shadow-cljs watch app
And go to 127.0.0.1:3000 My fork of the react-mde where the textarea component is being used is https://github.com/vitorqb/react-mde/blob/allows-customizing-textarea-component/src/components/TextArea.tsx#L320 Thanks!

lilactown 2020-04-23T18:06:39.220700Z

you’ll need to forward the ref somehow

lilactown 2020-04-23T18:06:50.221100Z

I don’t know how reactify-component really works

lilactown 2020-04-23T18:09:01.223100Z

but you might try something a little different:

(def textarea-component
  (-&gt; (fn textarea [props ref]
        (r/as-element [:textarea (assoc props :ref ref)]))
      (react/forwardRef)))

lilactown 2020-04-23T18:10:00.223800Z

this creates a React function component that renders a reagent :textarea element, and forwards the ref prop to the :textarea element

👍 1
Vitor Barbosa 2020-04-23T18:10:51.224700Z

Cool, I had never heard of this forwardRef strategy. I'll give it a try!

lilactown 2020-04-23T18:11:02.224900Z

I’m not sure if that’s enough to fix the cursor problem tho

lilactown 2020-04-23T18:11:22.225400Z

yeah, forwardRef is the underlying React mechanism for doing what you’re talking about: https://reactjs.org/docs/forwarding-refs.html

Vitor Barbosa 2020-04-23T18:12:16.226300Z

Cool! I'll do some experimentation see if I can get it working. Thanks!

Vitor Barbosa 2020-04-23T18:34:44.227700Z

@lilactown works like a charm! You just made my day, I was getting crazy after ~4h trying to get it working. Thanks!!!

lilactown 2020-04-23T18:34:59.227900Z

great to hear!

NoahTheDuke 2020-04-23T19:08:13.229300Z

in lieu of specific advice to the situation i posted earlier, anyone have "reagent architecture" tutorials or stuff? reagent's docs aren't super clear on how to structure an app

Jp Soares 2020-04-23T20:26:59.232800Z

I'm having the following errors in an app with reagent figwheel-main and material-ui. I tried a lot of things and it's probably because of the versions as I was using the same stack in a previous project with previous versions of reagent, react and material-ui.

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

The above error occurred in the &lt;WithStyles(ForwardRef(Button))&gt; component:
    in WithStyles(ForwardRef(Button))

Jp Soares 2020-04-24T13:04:14.234Z

It looks like I should change the material-ui elements to make it compatible with Reagent. Is that right?

Jp Soares 2020-04-24T14:05:14.234200Z

I see that with another react component it works, so the problem is specificaly with material-ui, but I was using it before with reagent.. I'll make a repo reproducing the problem in case someone can help me.

pcj 2020-04-24T18:03:49.236Z

You can use hooks in reagent components but is a bit cumbersome. Here is an example from a personal project:

(def CardInner
  (r/reactify-component
   (fn []
     [c/Paper "worked"])))

(def AnimatedDiv (oget animated "div"))

(defn Card
  []
  (let [[props set] (useSpring (fn [] #js{:x 0 :r 0}))
        x (oget props "x")
        r (oget props "r")
        bind (useDrag (fn [drag]
                        (let [down? (oget drag "down")
                              [mx] (oget drag "movement")]
                          (set #js{:x (if down? mx 0)
                                   :r (if down? (/ mx 50) 0)}))))]
    (r/create-element
     AnimatedDiv
     (oset! (bind) "!style" #js{:transform (interpolate #js[x r] #(str "translateX(" %1 "px) rotate(" %2 "deg)"))})
     (r/create-element CardInner))))

(defn use-card []
[:div 
(r/create-element Card)])

pcj 2020-04-24T18:08:37.236300Z

When doing this, remember to make sure the component isn't re-mounting all of the time.

Jp Soares 2020-04-27T01:05:50.275700Z

I'm actually seeing that I get this error when upgrading figwheel-main from 0.2.0 to 0.2.1. I posted a question in the figwheel-main channel https://clojurians.slack.com/archives/CALJ3BFLP/p1587949472108600