Hello, is there a better way of filtering a list in a reaction
, is it okay to use doall
?
(defn my-component []
(let [foo (r/atom [1 2 3])
bar (r/atom 2)
baz (reaction (doall (filter #(> % @bar) @foo))) ]
(fn []
....))
doall
should be fine. You can also use (into [] (filter ...) @foo)
if you need a vector - it uses a transducer, so it should be more efficient.
awesome thanks mate!
Hi, I am trying to wrap Autocomplete component from Material-ui in Reagent: https://material-ui.com/components/autocomplete/. Here is their example code:
<Autocomplete
id="free-solo-demo"
freeSolo
options={top100Films.map(option => option.title)}
renderInput={params => (
<TextField {...params} label="freeSolo" margin="normal" variant="outlined" />
)}
/>
I am wrapping this like this:
[:> Autocomplete {:options options
:getOptionLabel get-option-label
:renderOption render-option
:freeSolo true
:renderInput (fn [params]
(r/reactify-component [:> TextField (assoc (js->clj params)
:label label
:variant "outlined"
:on-change on-change)]
In particular, I am not sure how renderInput should be wrapped. It does not display any error but the component is empty, not showing anything.Yep, I've seen it as well in my own attempt to understand what was wrong. No clue, sorry.
Let me know if you find a solution.
I got it working like this
:renderInput (fn [props] (r/create-element Textfield props))
Where Textfield
is a native React component imported like this ["@material-ui/core/Textfield" :default Textfield]
(shadow-cljs)@valtteri Do you have any idea why as-element
does not work, what it does "wrong"?
First I dabbled with as-element
and reactify-component
but it turned out to be simpler to just not jump from ‘react-world’ into ‘reagent-world’
I’m not sure, but my best guess is that props
is a js-object and you need to clj->js
it and it looses the ref there for some reason.
Maybe @juhoteperi could give us the proper answer 🙂
I got it to render with (r/as-element [:> Textfield (js->clj props)])
but ref was lost or something and it died when trying to open the select list.
Huh, yeah.
With reactify-component
(.. props -inputProps -ref -current)
is the <input>
element. And with as-element
it somehow becomes null
. Even before we return from the function.
Nothing obviously wrong here. hmh.
You can try (r/reactify-component (fn [props] [:> TextField ...]))
That would be the correct way to use reactify-component, give the Reagent component as the parameter, not the elements vector.
Will it work, even though props
are expected to be a JS object?
Hmm, probably not. Bare function and as-element
should be best way here, as it doesn't try converting props.
Or hm, as-element also might do some conversion.
You could try (fn [props] (r/create-element TextField props))
yes that's it probably. as-element
will do some props conversion, but create-element
is just the React createElement
call
What conversions does as-element
do?
Oh, right... You cannot convert props at all if you're using react/createRef
. Because that mechanism expects that the underlying JS object is mutable.
@valtteri It does some custom version of clj->js
. And we also do the regular js->clj
. Thus, the ref object is not the same. Any mutation to it is not visible to the component that has set the ref in the first place.
Thanks guys for the explanations!
as-element
and reactify-component
do the same clj props map to js props map conversion, it might have some inference with Classes it doesn't know about
https://github.com/reagent-project/reagent/blob/master/src/reagent/impl/template.cljs#L62-L69 the first check probably only works for Plain Old Objects
But even if clj->js
is called for the ref value, it doesn't change it
@juhoteperi It's not about changing the value, it's about changing the holding object. TextField
is expected to call something like ref.current = this
in the depths of React. And if ref
is a new object created by clj->js
or the like, the Autocomplete
component will never see the change.
I might convert Reagent material-ui example to Shadow-cljs later and investigate more. Currently I don't have project with material-ui/labs running.
(let [a (react/createRef)]
(js/console.log (identical? a (clj->js a))))
This prints true
, ref object should be the same object still
To put it in other words - the above example will not work in raw React if you do something like props = deepCopy(props)
.
But if there is JS props -> clj props map conversion somewhere, that could do something else
Good point - as-element
doesn't work even without js->clj
.
Oh, duh - it doesn't work because it's not supposed to work with plain objects. It turns props
into {children: props}
.
I think, the case can be closed. :)
So if you use js->clj
that will definitely convert ref object to Cljs map, which break it
Autocomplete expects renderInput to be function returning React elements, not component. That might explain why reactify-component doesn't work, it returns class component.
Thx a lot for figuring this out.
Try to replace r/rectify-component
with r/as-element
.
Thx, that helped. What is the difference?
Docstrings to the rescue. :) The first one is to convert Reagent components (i.e. CLJS functions). The second one is to convert Hiccup to React elements.
I see.
It still fails when I try to click on the text field:
useAutocomplete.js:180 Uncaught TypeError: Cannot read property 'removeAttribute' of null
at useAutocomplete.js:180
at useEventCallback.js:26
at useAutocomplete.js:400
at useEventCallback.js:26
at useAutocomplete.js:426
at commitHookEffectListMount (react-dom.development.js:19765)
at commitPassiveHookEffects (react-dom.development.js:19803)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackImpl (react-dom.development.js:238)
at invokeGuardedCallback (react-dom.development.js:293)
(anonymous) @ useAutocomplete.js:180
(anonymous) @ useEventCallback.js:26
(anonymous) @ useAutocomplete.js:400
(anonymous) @ useEventCallback.js:26
(anonymous) @ useAutocomplete.js:426
commitHookEffectListMount @ react-dom.development.js:19765
commitPassiveHookEffects @ react-dom.development.js:19803
callCallback @ react-dom.development.js:189
invokeGuardedCallbackImpl @ react-dom.development.js:238
invokeGuardedCallback @ react-dom.development.js:293
flushPassiveEffectsImpl @ react-dom.development.js:22885
exports.unstable_runWithPriority @ scheduler.development.js:654
runWithPriority$1 @ react-dom.development.js:11062
flushPassiveEffects @ react-dom.development.js:22852
performSyncWorkOnRoot @ react-dom.development.js:21769
(anonymous) @ react-dom.development.js:11112
exports.unstable_runWithPriority @ scheduler.development.js:654
runWithPriority$1 @ react-dom.development.js:11062
flushSyncCallbackQueueImpl @ react-dom.development.js:11107
flushSyncCallbackQueue @ react-dom.development.js:11095
Internals.Events @ react-dom.development.js:21925
dispatchDiscreteEvent @ react-dom.development.js:1072
react_devtools_backend.js:6 The above error occurred in the <ForwardRef> component:
in ForwardRef (created by WithStyles(ForwardRef))
in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.suggestions.suggestions)
in orgpad.client.views.widgets.suggestions.suggestions (created by orgpad.client.views.share_orgpage.users)
in orgpad.client.views.share_orgpage.users (created by orgpad.client.views.share_orgpage.share_orgpage_dialog)
in orgpad.client.views.share_orgpage.share_orgpage_dialog (created by orgpad.client.views.widgets.md_dialog.md_dialog)
in div (created by ForwardRef)
in ForwardRef (created by WithStyles(ForwardRef))
in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.md_dialog.md_dialog)
in div (created by ForwardRef)
in ForwardRef (created by WithStyles(ForwardRef))
in WithStyles(ForwardRef) (created by ForwardRef)
in div (created by Transition)
in Transition (created by ForwardRef)
in ForwardRef (created by TrapFocus)
in TrapFocus (created by ForwardRef)
in div (created by ForwardRef)
in ForwardRef (created by ForwardRef)
in ForwardRef (created by ForwardRef)
in ForwardRef (created by WithStyles(ForwardRef))
in WithStyles(ForwardRef) (created by orgpad.client.views.widgets.md_dialog.md_dialog)
in orgpad.client.views.widgets.md_dialog.md_dialog (created by orgpad.client.views.share_orgpage.share_orgpage)
in orgpad.client.views.share_orgpage.share_orgpage (created by orgpad.client.views.root.modal_dialogs)
in orgpad.client.views.root.modal_dialogs (created by root-component)
in div (created by root-component)
in div (created by root-component)
in root-component (created by orgpad.client.views.root.root)
in orgpad.client.views.root.root
Consider adding an error boundary to your tree to customize error handling behavior.
Visit <https://fb.me/react-error-boundaries> to learn more about error boundaries.
r @ react_devtools_backend.js:6
logCapturedError @ react-dom.development.js:19561
logError @ react-dom.development.js:19598
expirationTime.callback @ react-dom.development.js:20742
commitUpdateQueue @ react-dom.development.js:12513
commitLifeCycles @ react-dom.development.js:19917
commitLayoutEffects @ react-dom.development.js:22835
callCallback @ react-dom.development.js:189
invokeGuardedCallbackImpl @ react-dom.development.js:238
invokeGuardedCallback @ react-dom.development.js:293
commitRootImpl @ react-dom.development.js:22573
exports.unstable_runWithPriority @ scheduler.development.js:654
runWithPriority$1 @ react-dom.development.js:11062
commitRoot @ react-dom.development.js:22413
performSyncWorkOnRoot @ react-dom.development.js:21839
(anonymous) @ react-dom.development.js:11112
exports.unstable_runWithPriority @ scheduler.development.js:654
runWithPriority$1 @ react-dom.development.js:11062
flushSyncCallbackQueueImpl @ react-dom.development.js:11107
flushSyncCallbackQueue @ react-dom.development.js:11095
Internals.Events @ react-dom.development.js:21925
dispatchDiscreteEvent @ react-dom.development.js:1072
react-dom.development.js:11125 Uncaught TypeError: Cannot read property 'removeAttribute' of null
at useAutocomplete.js:180
at useEventCallback.js:26
at useAutocomplete.js:400
at useEventCallback.js:26
at useAutocomplete.js:426
at commitHookEffectListMount (react-dom.development.js:19765)
at commitPassiveHookEffects (react-dom.development.js:19803)
at HTMLUnknownElement.callCallback (react-dom.development.js:189)
at Object.invokeGuardedCallbackImpl (react-dom.development.js:238)
at invokeGuardedCallback (react-dom.development.js:293)
Not sure whether the transformation I do on params is correct.