My experiences with Fable.Core.JSX #3996
shayanhabibi
started this conversation in
Show and tell
Replies: 1 comment
-
I've thought about the issue of default arguments being passed to JSX.Components in their implementations, and I'm not sure whether it is smart to preemptively treat them as existing outside of the ie. Instead of let Component ( props : IReactProperty list ) =
let properties =
match unbox props with
| null -> createEmpty
| _ -> !!props |> createObj
emitJsStatement properties "const {className, ...props} = $0" // this only captures all fields in `$props.props`
JSX.jsx $"""
<Component className={className} {{...props}} /> // we've only spread fields in `$props.props`
""" I account for potentially other outlier fields let Component ( props : IReactProperty list ) =
let properties =
match unbox props with
| null -> createEmpty
| _ -> !!props |> createObj // Now i'll capture all fields except for props into `...attrs`
emitJsStatement properties "const {className, ...props} = $0; const { props , ...attrs } = $props;"
JSX.jsx $"""
<Component className={className} {{...props}} {{...attrs}} /> // all fields captured and spread
""" Will give this a shot, if anyone has any other tips I'd appreciate it. Thanks! |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
My experiences with Fable and JSX
I am an amateur at JavaScript. Probably worse than an amateur. There are many poems that would justifiable express my distaste of JS and all things associated with it.
After grinding so many pointless hours of my life away trying to figure out how to do something such as creating a port/bindings for Shadcn so that I wouldn't have to create a single JScript file has really caused me grievance. I don't blame F# or Fable. I blame JScript. Through and through.
It would be fantastic if I didn't have to dip my toes into this festid pool, but that is apparently the only real way of deploying an SPA to the web without having to create the tools as I went along (which is what I think I've ended up doing).
This is just a dump of my experiences that hopefully might help someone else as nooby as myself (unlikely), or perhaps elicit some fantastical solution that would have saved me days of my life (and perhaps momentarily have me wish violence upon whatever genius shines that light onto my failure ;) ).
Motivation
My motivation on this journey was to create a port of Shadcn so that I could consume/customise a nice default set of components in Feliz style that had a well rounded set of relatively easily styled functional items.
This is simply because I do not know Html/JScript or anything of the sort, and the Feliz style was the most attractive method of building the UI. Naturally, I learned that there is no avoiding the tyranny of these technologies if you want to develop on the web. Fair enough (I miss systems programming though).
JSX on Fable
When I gave up on creating bindings for Shadcn-react libraries (because they were sub-par and lost any benefit of using Shadcn in customisability), I came across the JSX portion of Feliz and remembered reading on Fable that I could generate JSX from F#.
Fantastic!
Off I went to explore how to use this, and was unable to find anything on the web documentation to reflect on its usage.
My memory of JSX in Fable was from reading the blog posts. This was essentially all I had to go off.
Lets give it a test run...
Ah, no problem, this was discussed in the article; since Feliz consumes its own
ReactElement
type, and we are producingJSX.Element
s, we will have to just get the compiler to turn a blind eye since we are aware they are compatible.As per the blog, some easy helper funcs can keep things still well typed to a degree. I decided to make a little library for these helpers.
Ah, I had succesfully rendered an input with some styling that was interpolated in.
I did some extra naive tests such as passing reactelements in as a parameter to see if I could instantiate them within the JSX template. All went well.
The brain felt its momentary elation as I felt that this would make porting shadcn a breeze. Everyone's favourite copy pasta.
Spreads
Oh. There's a lot of this
{...props}
rubbish in the Shadcn JSX. What is this? I get what it is. But how am I supposed to utilise this pattern in Fable?The blog post is quite clear that interpolation can only be done in the position of attribute values and element children.
Also, unless I start passing anonymous functions as the parameter, I'm not sure that I can unpack the fields as they do.
Since I'm trying to bind to consume in feliz style, this won't work.
In the end, I couldn't find a better way than this to achieve the same features:
This can fail with JSX elements that are defined in the component library, but has the instantiation abstracted away from the consumer. If they are not inherently passed any attributes, then createObj will fail on a non-iterable variable. But that's okay, we can handle this with pattern matching
But this fails for the same reason. The transpiled file shows that JSX.Components are passed a $props parameter, and all arguments on the F# side are accessed as a field of this $props parameter. Best to just check against a null (will have to use
unbox
to allow this on props)This compiles fine, but in this case, the primitive I've imported from
@radix-ui
is not receiving its attributes correctly, and is not functioning.This is likely because whatever mechanism by which it is inheriting those attributes are not being passed to our component through the same
props
attribute that the JSX.Component interop uses.Nothing to do but unpack the
$props
argument in the transpiled code.This is making a massive assumption that the argument will always be
$props
. This is pretty harsh and makes all of this very poor to maintain.At this point, I'm tired of the
JSX.toReact
boilerplate at either the callsite, or requiring another definition with a different name.So not I just append an unbox ;_;
It's nice to use the interop of Fable outside of this very niche and particular use case.
Some things just absolutely work first try and fill me with enough confidence to fall off a cliff edge at the next implementation attempt:
This component is missing some of the latter works-arounds I stumbled through. It's also using some yucky methods of utilising the buttonVariants definition. This is just completely emitted and completely relies on transpilation keeping the name consistent.
But it was much easier to use at a call site where I could use an AnonRecord
Conclusion
If anyone has any ideas on how these can be implemented better, or safer, I would love to hear it!
Thanks for the awesome work you guys do!
Beta Was this translation helpful? Give feedback.
All reactions