I’m passing in the following component as a screen (react navigation) like so:
[:> (.-Screen Stack) {:name :Feed :options {:headerShown false}
:component (as-element [home])}]
where home is the following:
(defn home [{:keys [navigation] :as props}]
(dispatch [:load-videos 1])
[safe-area-view {:backgroundColor :white :flex 1}
[:<>
(if-not @(subscribe [:videos-loaded])
;; gif requires fresco library for android
[image {:source (js/require "../resources/images/loading.gif")
:style {:top 80 :left -115 :resizeMode :cover}}]
[flat-list {:refreshControl
(as-element
[refresh-control {:refreshing @(subscribe [:refreshing])
:onRefresh #(dispatch [:load-videos 1])}])
:data @(subscribe [:videos])
:renderItem (fn [item]
(as-element
[video-press (get (js->clj item) "item")
navigation]))
:keyExtractor (fn [item]
(.-uri item))
:initialNumberToRender 4
:maxToRenderPerBatch 2
:windowSize 5
:onViewableItemsChanged (.-current (useRef (fn [viewable-items changed] (js/console.log "viewable items " viewable items
"changed " changed))))
;;#(dispatch [:load-if-at-last %1 %2])
:style {:paddingLeft 10
:paddingRight 10}}])]])
However, I’m getting the following error:
Got an invalid value for 'component' prop for the screen 'Feed'. It must be a valid react component.
How can I fix this?also I want to use
home
as a functional component because it contains the useRef hook. So my guess is that
(as-element [:f> home])
should work. But I’m getting the same error in this case as wellReact component != React element. as-element
creates a React element. I'm assuming, of course, that :component
is named aptly and expects a React component (ah, right - the error explicitly states so).
so having (reactify-component home) gives
Invalid hook call. Hooks can only be called inside of the body of a function component
so what is the solution?
The warning speaks for itself, I think. home
has to be a proper function component.
Given that you cannot use reactify-component
with a function component, I see two options:
- Wrap home
in a regular Reagent component and use reactify-component
on it
- Use the old way to create a proper function component: https://github.com/reagent-project/reagent/blob/master/doc/ReactFeatures.md#pre-10-workaround
@p-himik what do you mean by “Wrap `home` in a regular Reagent component”?
(defn wrapped-home []
[:f> home])
(defn home []
...)
@p-himik that gives the following error:
Rendered more hooks than during the previous render
No idea.
I changed the useRef hook to useCallable, and now I get the same error:
Invalid Hook call. Hooks can only be called inside of the body of a function component...
This suggests that the all the parents of the component that contains the hook must be functional.In that case we can’t use reactify-component at all
> all the parents of the component that contains the hook must be functional This is false.
@ps calling reactify-component
on home
won't work, since Reagent will create a class component to wrap home
instead, treat home
like a React component:
(defn home [props]
(dispatch [:load-videos 1])
(as-element
[safe-area-view ,,,]))
this way you can use hooks inside of it
That was my very first message in this thread. :)
I was thinking you would then pass home
in directly to the Screen Stack. the extra layer and reactify-component to convert props makes sense tho
@ps another thing I noticed: you're using useRef
here to wrap the callback passed to onViewableItemsChanged
. why is that?
@lilactown This is what I have now:
(defn home [{:keys [navigation] :as props}]
(dispatch [:load-videos 1])
(as-element [safe-area-view {:backgroundColor :white :flex 1}
[:<>
(if-not @(subscribe [:videos-loaded])
;; gif requires fresco library for android
[image {:source (js/require "../resources/images/loading.gif")
:style {:top 80 :left -115 :resizeMode :cover}}]
[flat-list {:refreshControl
(as-element
[refresh-control {:refreshing @(subscribe [:refreshing])
:onRefresh #(dispatch [:load-videos 1])}])
:data @(subscribe [:videos])
:renderItem (fn [item]
(as-element
[video-press (get (js->clj item) "item")
navigation]))
:keyExtractor (fn [item]
(.-uri item))
:initialNumberToRender 4
:maxToRenderPerBatch 2
:windowSize 5
:onViewableItemsChanged viewable-ref
;;#(dispatch [:load-if-at-last %1 %2])
:style {:paddingLeft 10
:paddingRight 10}}])]]))
after wrapping with as-element, it seems that the :videos-loaded subscription inside isn’t workingI’m following the second answer in this to use onViewableItemsChange properly: https://stackoverflow.com/questions/48045696/flatlist-scrollview-error-on-any-state-change-invariant-violation-changing-on
yes, apologies. if you're passing home
in like this then it won't subscribe to your re-frame subs
basically I was trying to do in the flatlist:
:onViewableItemsChange #(dispatch […])
But this gives https://stackoverflow.com/questions/48045696/flatlist-scrollview-error-on-any-state-change-invariant-violation-changing-onthat's frustrating
so I wrapped it around useCallable
and that’s where we’re at
let's try what @p-himik suggested with a couple modifications:
(defn home [{:keys [navigation] :as props}]
(let [on-viewable-items-change (.-current (useRef ,,,))]
(dispatch [:load-videos 1])
[safe-area-view {:backgroundColor :white :flex 1}
[:<>
(if-not @(subscribe [:videos-loaded])
,,,)]]))
(defn wrapped-home [props]
[:f> home])
the error you were seeing before about "Rendered more hooks than during the previous render" was due to having the useRef
call inside of the if
expression
hooks calls must be static across renders - you cannot put them inside loops or conditionals
the wrapped-home
can be reactify-component
and passed in like you did before
thanks @lilactown @p-himik
Hey gals and guys, What is the easiest way to debug a component in order to understand why it gets re-rendered please? (I read https://github.com/reagent-project/reagent/blob/master/doc/WhenDoComponentsUpdate.md) This is my component:
(defn- regular-cells
[& {:keys [entity]}]
(js/console.log "regular-cells" entity)
[:div
{:on-click #(do (js/console.log "clicked")
(dispatch [:my.ns.events/clicked entity]))}
(str entity)])
When I click on the div, it has the effect of sending a PUT request to an API, then a GET to that same API which returns a new value for entity
.
Here is what I get in the logs:
regular-cells {:id 1955, :enabled false}
clicked
regular-cells {:id 1955, :enabled false}
regular-cells {:id 1955, :enabled false}
regular-cells {:id 1955, :enabled true}
I don't understand why it gets re-rendered 3 times instead of 1 after clicked
if the only param hasn't changed?How is regular-cells
used?
If it's used as (regular-cells ...)
, then replace ()
with []
.
If it's used with []
, then it's probably because the :on-click
function get recreated on each render of regular-cells
. I don't know all the inner workings of this particular issue, but there have been numerous discussions about it.
I believe there are hooks that fix it, but I'm not sure whether using them for such a simple component would be worth it. It could also be fixed by using a cache manually, which is also more trouble than it might be worth in this particular case.
If you confirm that it's indeed the latter, you can @ lilactown - I remember him having this discussion with someone else recently.
Thank you @p-himik, I'll check how regular-cells
and continue my investigation 🙂
I finally nailed it
What was it?
I had something like this:
(into [:div]
(cond-> [[regular-cells :entity @entity]]
@show? (merge [:div "..."] [:div "..."])))
And moving the @show? ...
upper in the DOM fixed it. I'm not 100% sure why.Not sure either. But this particular code makes me squint in suspicion. The contract of merge
says that it's to be used only with maps. But you're passing it vectors. I have no clue what you want to get out of it, but it's definitely not a correct usage.
Perhaps, you want to use into
instead.
(cond-> [:div [regular-cells ...]]
@show? (conj [:div ...] [:div ...]))
Using into
instead of merge
if for sure a better practice. It doesn't resolve the issue though (tested)
Sure, I didn't mean that it would help with the initial problem. It's just that using merge
on anything other than maps is an undefined behavior.
Oh ok, thank you for pointing that out 🙂