A minimalistic ClojureScript interface to React.js
otwieracz 2020-03-29T08:10:03.017800Z

How can I generate custom methods for Reagent/React component?

otwieracz 2020-03-29T08:10:42.018300Z

I need to provide getValue() and getInputNode() for my component to be used by something else.

juhoteperi 2020-03-29T08:15:40.018900Z

@slawek098 Are those instance methods or static methods? If instance methods, use r/create-class

otwieracz 2020-03-29T08:18:06.020700Z

Instance methods.

juhoteperi 2020-03-29T08:18:26.021300Z

I think docs don't currently have section just for this, but other parts have examples of this: here get-derived-state-from-error is static method (it doesn't take this parameter) but it works because Reagent knows the name and has special handling. If you need to add static properties currently you'd need to take the return value from create-class and attach the properties to that.

juhoteperi 2020-03-29T08:19:06.022Z

Just add :get-value (or :getValue) etc. to the create-class parameter, should work automatically.

otwieracz 2020-03-29T08:19:37.022600Z

Hm, I alredy tried it.

juhoteperi 2020-03-29T08:20:12.023700Z

If the method take React props or other parameters, you will get the unmodified values from React, which might be a bit different to what default methods show as Reagent converts some parameters.

otwieracz 2020-03-29T08:20:16.023900Z

(defn editor [data]
   {:display-name "editor"
    :getInputNode (fn [& args] (js/console.log (str args)))
    :reagent-render (fn [data]
                      [:div (str data)])}))

otwieracz 2020-03-29T08:20:37.024300Z

I prepare list of columns with map:

otwieracz 2020-03-29T08:20:45.024600Z

(map (fn [id text]
           {:key       id
            :name      text
            :editable  true
            :formatter (r/reactify-component formatter)
            :editor    (r/reactify-component editor)})

otwieracz 2020-03-29T08:20:51.024800Z

This should reasemble code from:

juhoteperi 2020-03-29T08:21:59.026200Z

Do you see the column headers etc? So the column options are working?

otwieracz 2020-03-29T08:22:11.026400Z


class ColorEditor extends React.Component {
  getInputNode() {
    return ReactDOM.findDOMNode(this).getElementsByTagName("input")[0];

const columns = [
  { key: "id", name: "ID" },
  { key: "title", name: "Title" },
  { key: "labelColour", name: "Label Colour", editor: ColorEditor }

otwieracz 2020-03-29T08:22:33.026600Z

Yes, everything else is working.

otwieracz 2020-03-29T08:22:37.026800Z

Formatter is also working.

otwieracz 2020-03-29T08:23:00.027Z

But here I am getting:

otwieracz 2020-03-29T08:23:05.027400Z

react-data-grid.js:2 Uncaught TypeError: n.getEditor(...).getInputNode is not a function

juhoteperi 2020-03-29T08:23:07.027500Z

And you are using latest Reagent?

otwieracz 2020-03-29T08:23:26.027900Z


otwieracz 2020-03-29T08:23:29.028200Z

let mee see which is latest..

otwieracz 2020-03-29T08:23:43.028600Z

Yes, it's the most recent.

juhoteperi 2020-03-29T08:24:04.029100Z

reactify-component might be unncessary here, because create-class already returns Component class, no need to convert it. But might be it just doesn't do anything, don't remember.

juhoteperi 2020-03-29T08:25:01.029400Z

Try :editor (editor "foobar)

otwieracz 2020-03-29T08:30:14.030Z

Why (editor "foobar")?

juhoteperi 2020-03-29T08:30:40.030600Z

(You could also change editor from defn to def, and remove the call and parameter)

otwieracz 2020-03-29T08:31:14.031500Z


otwieracz 2020-03-29T08:31:18.031800Z

somethin is working!

juhoteperi 2020-03-29T08:31:51.032600Z

If I remember correctly, reactify-component when provided with function value (`editor`) will convert the function to Reagent class component, and that is not the same class as what you create inside the function. The render of this created class is rendered using the class you created.

juhoteperi 2020-03-29T08:32:07.033Z

So in this case, the parent component doesn't see the class you created but Reagent wrapper class.

juhoteperi 2020-03-29T08:32:19.033400Z

So you need to directly provide the class you create.

juhoteperi 2020-03-29T08:34:09.034300Z

(def editor (r/create-class ...)) ... :editor editor is probably the best way to handle this case

juhoteperi 2020-03-29T08:44:28.035300Z

Documented now: (hopefully the explanations make some sense)

otwieracz 2020-03-29T08:49:30.035600Z

Thank you very much!

hindol 2020-03-29T12:06:59.037600Z

Hi, does Reagent support dynamic variables as a mechanism to communicate across the stack? I want to pass a callback from the grand-parent to grand-child but the component in-between does not use it.

p-himik 2020-03-29T12:10:05.037700Z

Not an expert here, but I would guess that it doesn't. A child component may be re-rendered without the re-rendering of the parent, thus making the dynamic variable not bound to the correct value. I would just wrap and create the grandchild right there in the parent, and pass the wrapped component to the opaque child.

hindol 2020-03-29T12:13:46.037900Z

Thanks for the advice! Actually I simplified a bit while asking the question. There is probably 2/3 levels which don't use it. Passing anything down is a pain. I don't want to immediately jump to re-frame if I can help it.

p-himik 2020-03-29T12:21:55.038100Z

You can still use a wrapper, no matter how nested the structure is. Well, unless you dynamically construct the structure and pass it down as a parameter to the parent component.

juhoteperi 2020-03-29T12:25:36.038300Z

Not really. The component render function can be called several times etc. not all times will have the same dynamic context.

juhoteperi 2020-03-29T12:26:48.038500Z

React context, however, should be good fit for this:

markx 2020-03-29T21:17:46.039700Z

Hi! I’m reading the reagent tutorial. Here it says component-did-update will not be called when re-render is triggered by ratom change. Is this true?


If you make a print statement inside your reagent render, you will see that your component is recreated again everytime you click on the button. So compnoent did update is called because it is called everytime reagent-render is called.


(defn test-comp [counter]
   {:display-name "test-comp"
    (fn [this]
      (prn "component did update"))
    (fn []
       [:button {:on-click  #(swap! counter inc)} "click"]])}))

(defcard-rg component-test
  (let [counter (r/atom 0)]
    (fn []
       [:div @counter]
       [test-comp counter]])))


you can see that Created is only called once, and component did update is never called whenever you click the button.


I dunno if this answer your question.


but I guess it makes sense that did updated is not called because the reference (the atom) is not change.

markx 2020-03-31T02:28:49.042400Z

And your second example, sorry but I’m not sure what you are trying to say here. It doesn’t even deref the atom, so sure it will never update.


Ok, sorry for being confusing. I think you should try to construct an exemple where component-did-update is called without being re-rendering and then you could construct an exemple where your atom would not trigger the said methods.


About render, you could use the component -did-mount method to check if it is created again?

markx 2020-03-29T21:18:54.040100Z

(defn test-comp []
  (let [counter (r/atom 0)]
      {:display-name "test-comp"
       (fn [this]
         (prn "component did update"))

       (fn []
          [:div @counter]
          [:button {:on-click  #(swap! counter inc)} "click"]])})))

markx 2020-03-29T21:19:31.040300Z

I tried with this simple component, and apparently it does trigger! What’s wrong here?