How can I generate custom methods for Reagent/React component?
I need to provide getValue()
and getInputNode()
for my component to be used by something else.
@slawek098 Are those instance methods or static methods? If instance methods, use r/create-class
Instance methods.
I think docs don't currently have section just for this, but other parts have examples of this: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#error-boundaries 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.
Just add :get-value
(or :getValue
) etc. to the create-class parameter, should work automatically.
Hm, I alredy tried it.
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.
(defn editor [data]
(r/create-class
{:display-name "editor"
:getInputNode (fn [& args] (js/console.log (str args)))
:reagent-render (fn [data]
[:div (str data)])}))
I prepare list of columns with map
:
(map (fn [id text]
{:key id
:name text
:editable true
:formatter (r/reactify-component formatter)
:editor (r/reactify-component editor)})
(range)
column-names)
This should reasemble code from:
https://adazzle.github.io/react-data-grid/docs/examples/custom-editors
Do you see the column headers etc? So the column options are working?
So:
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 }
];
Yes, everything else is working.
Formatter is also working.
But here I am getting:
react-data-grid.js:2 Uncaught TypeError: n.getEditor(...).getInputNode is not a function
And you are using latest Reagent?
0.10.0
let mee see which is latest..
Yes, it's the most recent.
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.
Try :editor (editor "foobar)
Why (editor "foobar")
?
(You could also change editor
from defn
to def
, and remove the call and parameter)
hah
somethin is working!
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.
So in this case, the parent component doesn't see the class you created but Reagent wrapper class.
So you need to directly provide the class you create.
(def editor (r/create-class ...)) ... :editor editor
is probably the best way to handle this case
Documented now: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#component-classes (hopefully the explanations make some sense)
Thank you very much!
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.
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.
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.
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.
Not really. The component render function can be called several times etc. not all times will have the same dynamic context.
React context, however, should be good fit for this: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#context
Hi! I’m reading the reagent tutorial. https://cljdoc.org/d/reagent/reagent/0.10.0/doc/tutorials/when-do-components-update-#lifecycle-functions
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]
(r/create-class
{:display-name "test-comp"
:component-did-update
(fn [this]
(prn "component did update"))
:reagent-render
(fn []
[:div
[: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.
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?
(defn test-comp []
(let [counter (r/atom 0)]
(r/create-class
{:display-name "test-comp"
:component-did-update
(fn [this]
(prn "component did update"))
:reagent-render
(fn []
[:div
[:div @counter]
[:button {:on-click #(swap! counter inc)} "click"]])})))
I tried with this simple component, and apparently it does trigger! What’s wrong here?