@lilactown Do you advise some integration with css-in-js like $ ? Material-ui for example, it bundles a js-in-css solution https://gist.github.com/geraldodev/a9b60dd611d1628f9413dd6de6c3c974#file-material_ui_helix-cljs-L14 . It avoids global css which is a good thing.
I'm playing with Helix and React Native, I'm trying to understand the error here:
(apply $ rn/View {:style #js {:flex 1 :alignItems "center" :justifyContent "center"}}
[(rnc/text {:style #js {:fontSize 36}} "Hello Helix!")
(rnc/button {:title "Click me"})])
the code is for experimenting purposes (trying to wrap react native stuff with factory
)
but that code doesn't work, it errors with [object Object] is not ISeqable
the stack from RN is quite incomplete, so not sure, I was using the apply $
to isolate the constructor
when I use createElement
directly from React it works fine
digging a bit, the problem is happening during -native-props
call
doing this (apply helix.core/$ rn/Text {:style #js {:flex 1}} ["Hello" "World"])
works for me so I imagine the error has to do with the component children
@alidcastano I ran a couple of experiments to test that, but in the end, when I kept the props clean, it failed, when used the :style, it broke
was specifically problem with rn/View
from React Native from MacOS, and I can see a difference, the View is a string component (native component), the others are not
so I'm guessing the issue is with the fn
version of $
when used with native elements
you can break any with: (apply $ "div" {:style #js {:flex 1}} ["child"])
actually, I'm writing an issue, but as I test more (not on RN now, just on regular react on browser) I see it breaks with this: ($ "div" {:style #js {:flex 1}})
you might want to pass ^:native
to $
that might fix it
@alidcastano I figured a different problem, not really an issue per se, but a confusing situation
but this looks to still be a bug
I described that in: https://github.com/Lokeh/helix/issues/61
@wilkerlucio is there a particular reason youāre passing in styles as a JS object instead of a map?
the trick part is because its not clear with RN components are native and which are not
and that may vary depending on which RN impl its being targetted (mac os RN may have different settings than Windows, than ios, etc... and those can change over time)
the idea is that if youāre using $ then you should be able to just passing in maps
so Iām not sure yet why youād want to literally pass in a JS obj yet
for example, Button
in RN for MacOS is not a native component
what do you mean by ānativeā exactly?
the same thing you mean in helix when you check for native when running $
if its a string or keyword
or if its a component
okay, itās a bit of a leaky thing beyond react-dom so thatās why iām asking
gotcha
yeah, so using the js-obj could be the safe version for incosistent situations like this
yeah thereās a couple ways to handle this, at the application level and at the library (helix level)
one thing you can do is annotate the Button
symbol with ^:native
e.g. ($ ^:native rn/Button {:style {:flex 1}})
but then I have to know which component is which (native or not)
if I could just throw #js in all of then, I can avoid caring about it
also, I fear changes between versions, a non-native component today may be turned native on the next version
the ānativeā classification is not really useful beyond react-dom
I regret adding it tbh
the only thing that really matters is whether a component expects :style
as a JS object or not
so all react-native components are ānativeā in that sense
yeah, but in impl that varies, which is the issue (which is not true for react dom)
when you use the $ macro it doesnāt really tho
($ foo)
is always assumed to be non-ānativeā
because itās a symbol
yeah, I only have problems when using props with :style
ah, sorry, gotcha
yeah, I got the problem because I'm wrapping the components with factory
it sounds like e.g. rn/View
is actually a string?
yup
šµ
that's what I mean by native š
I never expected that
rn/View
=> "RCTView"
rn/Text
=>
#js{"$$typeof" "Symbol(react.forward_ref)",
:render #object[Text],
:displayName "Text",
:propTypes #js{:ellipsizeMode #object[bound checkType],
:numberOfLines #object[bound checkType],
:textBreakStrategy #object[bound checkType],
:onLayout #object[bound checkType],
:onPress #object[bound checkType],
:onLongPress #object[bound checkType],
:pressRetentionOffset #object[bound checkType],
:selectable #object[bound checkType],
:selectionColor #object[bound colorPropType],
:suppressHighlighting #object[bound checkType],
:style #object[Function],
:testID #object[bound checkType],
:nativeID #object[bound checkType],
:allowFontScaling #object[bound checkType],
:maxFontSizeMultiplier #object[bound checkType],
:accessible #object[bound checkType],
:adjustsFontSizeToFit #object[bound checkType],
:minimumFontScale #object[bound checkType],
:disabled #object[bound checkType],
:dataDetectorType #object[bound checkType]}}
rn/Button
=> #object[Button]
wild
okay
I donāt really exercise the runtime version of $ much
no worries, I'm just a new user hitting unexplored code paths š
FWIW what Iād like to do is create a namespace of macros similar to helix.dom
@lilactown and you plan to also support #js style on native?
I think the macro ns may be a problem, because you can't know what to expect from the components
even though Button
is not native on MacOS RN, it may be native in other impls
so we can't know for sure what each is, makes sense?
@wilkerlucio using a macro routes around this problem completely
the macro $ is completely consistent
($ ^:native rn/View {:style {flex 1}})
will always convert the :style
prop to a JS object
$ checks at macro time whether the first argument is a string/keyword or a symbol
if itās a symbol, it treats it as ānon-nativeā unless itās annotated with ^:native
metadata
so rn/View
can go from at runtime being a string, function, class, memoized, whatever, it doesnāt matter
a macro would do something like:
(defmacro view
[& args]
`($ ^:native rn/View ~@args))
which would always be unambiguous. no matter what rn/View
is at runtime, it would always have the same behavior
letās define native to be:
āa component which requires transformation of props before construction, e.g. :style
prop to be a JS object, :class
to be renamed to :className
, etc.ā
then whether or not rn/View
is a string or not is irrelevant, it will always be ānativeā. the detection of string/keyword is just a dumb heuristic to try and get that information at compile time
the inconsistency weāre running into now is that if you apply this same heuristic at runtime, itās even dumber, because apparently React Native has some of itās ānativeā components as strings and other ānativeā components as functions / memoized components /etc.
so the easy answer is: donāt do this at runtime :P
but Iāll have to think more about this issue before coming up with an encompassing solution
I wonāt be at a computer today, so fix will have to wait until later this week
for now, I can probably put a bandaid on it by not blowing up if you pass in a JS object
but ultimately I need to think harder on the API of $
and how it extends beyond react-dom