-
Notifications
You must be signed in to change notification settings - Fork 83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
My journey with Feliz | A comparison between Fable.React and Feliz #155
Comments
Great analysis. For me personally, even though the double list formatting is a pain sometimes, I feel that looking at Fable.React code I get to see the structure of the underlying html better. It's div [.. , not Html.div [.... (etc). It's not a big thing, but my brain seems to have an easier parsing Fable.React. |
Great write up @MangelMaxime, you made a lot of good points. From what I've gathered the main takeaway of this is mostly issues with how the library is extended, and the problems that occur when not done optimally. I can definitely agree that writing libraries for I've written a fair number of extensions for OverloadsI use Visual Studio and have zero problems finding which overloads are available to me, maybe this is something that can be improved on ionide's end? We could just make Callbacks:Instead of: [<Erase>]
type editor =
/// <summary>Triggered when the Editor has been mounted</summary>
static member inline editorDidMount (f : System.Func<Monaco.Editor.IStandaloneCodeEditor, Monaco.IExports, unit>) = Interop.mkAttr "editorDidMount" f Write: [<Erase>]
type editor =
/// <summary>Triggered when the Editor has been mounted</summary>
static member inline editorDidMount (f : Monaco.Editor.IStandaloneCodeEditor -> Monaco.IExports -> unit) = Interop.mkAttr "editorDidMount" (Func<_,_,_> f) You can then call the property like you would expect on the client side. Writing invalid codeI haven't seen anyone else apply this quite to the extent I have in some of my libraries, and may not be entirely applicable when extending Click to expand[<AutoOpen;EditorBrowsable(EditorBrowsableState.Never)>]
module Types =
type IPlotProperty = interface end
type IModeBarButtonsProperty = interface end
type IAaxisProperty = interface end
type IAggregateProperty = interface end
type IAggregationProperty = interface end
type IAggregationsProperty = interface end
type IAngularaxisProperty = interface end
type IAnimationProperty = interface end
type IAnnotationProperty = interface end
type IAnnotationsProperty = interface end
type IAreaProperty = interface end
type IAspectratioProperty = interface end
type IAxisProperty = interface end
type IBarProperty = interface end
type IBarpolarProperty = interface end
type IBaxisProperty = interface end
type IBorderProperty = interface end
type IBoxProperty = interface end
type IButtonProperty = interface end
type ICameraProperty = interface end
type ICandlestickProperty = interface end
type ICapsProperty = interface end
type ICarpetProperty = interface end
type ICaxisProperty = interface end
type ICellsProperty = interface end
type ICenterProperty = interface end
type IChoroplethProperty = interface end
type IChoroplethmapboxProperty = interface end
type ICircleProperty = interface end
type IColoraxisProperty = interface end
type IColorbarProperty = interface end
type IColorscaleProperty = interface end
type IColorscalesProperty = interface end
type IConcentrationscalesProperty = interface end
type IConeProperty = interface end
type IConfigProperty = interface end
type IConnectorProperty = interface end
type IContourProperty = interface end
type IContourcarpetProperty = interface end
type IContoursProperty = interface end
type ICumulativeProperty = interface end
type ICurrentvalueProperty = interface end
type IDecreasingProperty = interface end
type IDeltaProperty = interface end
type IDensitymapboxProperty = interface end
type IDiagonalProperty = interface end
type IDimensionProperty = interface end
type IDimensionsProperty = interface end
type IDomainProperty = interface end
type IEditsProperty = interface end
type IErrorXProperty = interface end
type IErrorYProperty = interface end
type IErrorZProperty = interface end
type IEyeProperty = interface end
type IFillProperty = interface end
type IFilterProperty = interface end
type IFontProperty = interface end
type IFrameProperty = interface end
type IFramesEntryProperty = interface end
type IFramesProperty = interface end
type IFunnelProperty = interface end
type IFunnelareaProperty = interface end
type IGaugeProperty = interface end
type IGeoProperty = interface end
type IGradientProperty = interface end
type IGridProperty = interface end
type IGroupbyProperty = interface end
type IHeaderProperty = interface end
type IHeatmapProperty = interface end
type IHeatmapglProperty = interface end
type IHistogram2dProperty = interface end
type IHistogram2dcontourProperty = interface end
type IHistogramProperty = interface end
type IHoverlabelProperty = interface end
type IImageProperty = interface end
type IImagesProperty = interface end
type IIncreasingProperty = interface end
type IIndicatorProperty = interface end
type IInsidetextfontProperty = interface end
type IIsosurfaceProperty = interface end
type ILabelfontProperty = interface end
type ILataxisProperty = interface end
type ILayerProperty = interface end
type ILayersProperty = interface end
type ILayoutProperty = interface end
type ILeafProperty = interface end
type ILegendProperty = interface end
type ILightingProperty = interface end
type ILightpositionProperty = interface end
type ILineProperty = interface end
type ILinkProperty = interface end
type ILonaxisProperty = interface end
type IMapboxProperty = interface end
type IMarginProperty = interface end
type IMarkerProperty = interface end
type IMeanlineProperty = interface end
type IMesh3dProperty = interface end
type IModebarProperty = interface end
type INodeProperty = interface end
type INumberProperty = interface end
type IOhlcProperty = interface end
type IOutsidetextfontProperty = interface end
type IPadProperty = interface end
type IParcatsProperty = interface end
type IParcoordsProperty = interface end
type IPathbarProperty = interface end
type IPieProperty = interface end
type IPointcloudProperty = interface end
type IPolarProperty = interface end
type IProjectProperty = interface end
type IProjectionProperty = interface end
type IRadialaxisProperty = interface end
type IRangebreakProperty = interface end
type IRangebreaksProperty = interface end
type IRangefontProperty = interface end
type IRangeselectorProperty = interface end
type IRangesliderProperty = interface end
type IRotationProperty = interface end
type ISankeyProperty = interface end
type IScatter3dProperty = interface end
type IScatterProperty = interface end
type IScattercarpetProperty = interface end
type IScattergeoProperty = interface end
type IScatterglProperty = interface end
type IScattermapboxProperty = interface end
type IScatterpolarProperty = interface end
type IScatterpolarglProperty = interface end
type IScatterternaryProperty = interface end
type ISceneProperty = interface end
type ISelectedProperty = interface end
type IShapeProperty = interface end
type IShapesProperty = interface end
type ISlicesProperty = interface end
type ISliderProperty = interface end
type ISlidersProperty = interface end
type ISortProperty = interface end
type ISpaceframeProperty = interface end
type ISplomProperty = interface end
type IStartsProperty = interface end
type IStepProperty = interface end
type IStepsProperty = interface end
type IStreamProperty = interface end
type IStreamtubeProperty = interface end
type IStyleProperty = interface end
type IStylesProperty = interface end
type ISunburstProperty = interface end
type ISurfaceProperty = interface end
type ISymbolProperty = interface end
type ITableProperty = interface end
type ITernaryProperty = interface end
type ITextfontProperty = interface end
type IThresholdProperty = interface end
type ITickfontProperty = interface end
type ITickformatstopProperty = interface end
type ITickformatstopsProperty = interface end
type ITilingProperty = interface end
type ITitleProperty = interface end
type ITotalsProperty = interface end
type ITracesProperty = interface end
type ITransformsProperty = interface end
type ITransitionProperty = interface end
type ITreemapProperty = interface end
type IUniformtextProperty = interface end
type IUnselectedProperty = interface end
type IUpProperty = interface end
type IUpdatemenuProperty = interface end
type IUpdatemenusProperty = interface end
type IViolinProperty = interface end
type IVolumeProperty = interface end
type IWaterfallProperty = interface end
type IXProperty = interface end
type IXaxisProperty = interface end
type IXbinsProperty = interface end
type IYProperty = interface end
type IYaxisProperty = interface end
type IYbinsProperty = interface end
type IZProperty = interface end
type IZaxisProperty = interface end
type IButtonsProperty = inherit IModeBarButtonsProperty
type IMeasureProperty = interface end
type ITemplateProperty = interface end In a similar vein, you can also implement things in the library code to "automagically" combine the properties if you want. I'm not sure how this works when using things outside your own library though. The trick to this is to make those items a different type than just ConclusionI think much of the pain you had could be solved with better documentation on how to navigate writing your own libraries in the "Feliz" way. Maybe @Zaid-Ajaj and I can work on that in the future. |
Thank you @l3m
Indeed, switching how to read the code is not easy and that's why I mentioned we should not see it as HTML in my case it's helped me make the switch. I suppose that with F# 5 and the ability to "open static classes" Feliz will be able to support both Thank you @Shmew for the comment and indeed I forgot that there are several types of "libraries" for Feliz. About the "Writing invalid code" yes but in some libraries, we do want to "extends" They are just code that injects a class by default in the HTML element.
In the case of a library which works on "real" components (sorry don't know how to name it differently) your solution is indeed the right one. For example, this is the approach taken by Fable.ReactLeaflet where I am defining the specific properties for each component. And thus components don't have to support all the standard HTML properties. About your solution for "Callbacks" not it didn't work for me. But the function passed by Fable was having curried arguments and unless I wanted to write something like If you know how to make it work I will be happy to see a PR sent to fix it in https://github.com/fable-compiler/repl/pull/108/files |
Hi @MangelMaxime, First of all, I want to thank you for taking the time and effort not just to try out Feliz on a project but also to write such an extensive analysis. I am sure many of Fable users will find it very helpful and informative. I really appreciate that this kind of feedback and openness and believe it is crucial for to make this library the best it can be along with its ecosystem 🙏 I will talk about Feliz.Bulma in a moment because I think it is kind of special in the ecosystem, First I will answer to some issues you have that can already be fixed without changes in the library. Overloads of
|
Indeed, I didn't think about this usage.
Oh sorry, I didn't saw the end of the snippet provided by @Shmew. I just saw the signature declaration and was like no doesn't work. You are both right, we can do the transformation before passing the value to the property. The case of Feliz.BulmaYes, TypedCssClasses is ok but compared to Fulma or Feliz.Bulma it can't provide the same level of features. Because not everything is contained in the CSS file. Another thing is that in dev mode, we don't always have the file generated on disk and I am not sure if TypedCssClasses support this scenario.
I do agree, I just think we need to add a bit of the work done in Fulma to make it similar. Like I said we need to find the correct middle ground between both of them. The missing features I have in mind right now are allowing to control the output element and make it easier to extends the set of supported colour. I don't think it will add much complexity over it. @Zaid-Ajaj thank you for the feedback and especially for pointing me the right direction on how to use Feliz potential. |
Thanks @MangelMaxime - that's a great comparison. You haven't mentioned Fantomas anywhere for auto formatting the code - did you have a chance to work with Fantomas with either Fable.React or Feliz? |
@theimowski I didn't use Fantomas since some times. Mostly because it was messing with my code and making it really hard to read. From my experience to was not playing well with Fable.React and Feliz. I also don't like the default of Fantomas I need to check it again in the future when I know how I want my code to be indented. But, from what I looked at the repo I don't think it will fit my needs mostly because of Fantomas formatting the code depending on the size of context. For example, if I write something like that: type Test =
{
Prop1 : string
} I do want it to stay like that and not become Perhaps a record is not a good example, but I hope you understand what I mean :) |
@theimowski that's great to hear, I use fantomas on my non-Fable projects. I'll have to give it another shot and see how it handles it. |
@MangelMaxime thanks for your work. Somehow your Fulma project attacked me to the fable world. And I really enjoy the journey. I also like the single list but I also like DU than classes member overrides. Sometimes I wonder if DU supports override then it would be super great. let myView =
div </> [
Classes [ ""; ""; "" ]
Children [
...
]
] The operator is pretty simple and also take advantage of DU, you can check my rough implementation here: https://github.com/albertwoo/Fun.LightForm/blob/master/src/Fun.LightForm.Fable/Fable.React.Extra.fs @Zaid-Ajaj Thanks for your great work too, I also started to convert some of my projects to Feliz and Feliz.Material. Thanks! |
Hi @MangelMaxime, I already created new Issue in Anyway, thank you again - great analysis! |
At the risk is reopening old discussions (sorry!) I just wanted to add my two cents on the indentation debate - this is (more or less) the styling we follow which does allow for nice, consistent identation and following for the Fable.React style. The one place this doesn't quite apply to, however, is for non-trivial styling. However, in our experience this accounts for only a minority of elements (maybe 10-20%). div [ ] [
Hero.hero [ Hero.IsFullHeight ] [
Hero.body [ ] [
Container.container [ ] [
img [ Src "img/fable-ionide.png"
Style [ Display DisplayOptions.Block
Width "auto"
Margin "auto" ]
]
br [ ]
Heading.h3 [ Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] [
str "Fable REPL"
]
Heading.p [ Heading.IsSubtitle
Heading.Is5
Heading.Modifiers [ Modifier.TextAlignment (Screen.All, TextAlignment.Centered) ] ] [
str "is only available on desktop"
]
]
]
]
] Also, if you use VS Code and something like Rainbow Brackets, it can really help avoid the bracket overload. |
Something else to consider (maybe I missed this above) is that GiraffeViewEngine uses the Fable.React style i.e. element attr-list children-list, so just something to think about in terms of people transitioning between the two. @Zaid-Ajaj makes a good point about Femto; is there any reason why this couldn't be implemented by Fable.React-style components, or is there something inherent about Feliz style that Femto is coupled to? |
Libraries derived from Fable.React could (and should) add Femto metadata but the Fable.React itself couldn't because it has an incompatible mix of What this means for libraries derived from Regarding formatting: Fantomas is not able yet to format either DSLs in a sensible way and requires some work on making list formatting configurable or have some kind of "Feliz-mode" flag to format the React DSL using nice defaults (which we have to come up with) |
@Zaid-Ajaj The issue with React npm dependencies is also more complex as it should be possible to use preact instead of react, without breaking Femto. |
@l3m Indeed, but in this case we could make an addition to Femto to support this case. It remembers me of something so perhaps we have an issue about that in Femto repo (sorry don't have the time to check right now ^^) |
@l3m Preact can be used in place of React regardless of Femto npm metadata because you only need to configure module import aliases as per the guidelines in Switching to Preact when you have an existing React application. Building a pure Preact binding (100% non-React code) is not the goal of Feliz and should probably be implemented as a standalone library (Feliz.Preact that doesn't depend on Feliz) from which Preact-only bindings will be extended and used in case you want to stay away from |
@Zaid-Ajaj I'm using preact right now in my Elmish app and it works well. I was just wary about Femto modifying packages.json if it sees a nuget with React deps and I wanted to highlight that. If that's not a problem, all the better. Also, |
I'm indenting code in a very similar way from Isaac's, and its pretty readable.
That would be super cool. Would it be possible to make the Html class static so it could be opened? |
@fc1943s |
How would opening the static class hinder discovery? You could still type Feliz.Html.<Ctrl-.> when you want to discover what's available. |
@MaxWilsonMS That required additional steps to output the "final" code. And you would write What's good is that people can choose to open the static class or not depending on their preferences. :) |
Not sure how useful this will be since I'm echoing what has been said above, but experiences as a complete beginner:
Thanks to all for sharing these libraries! |
Thanks for your input @drk-mtr!
Yeah, it can appear pretty daunting at first, but it's pretty simple once you get used to it. That being said it is a fair bit of initial cost for the developer when writing libraries. I find this a pretty acceptable trade-off since the vast majority of users aren't going to be writing libraries. Luckily the work itself is pretty simple, and some copy + paste can speed up a lot of the work (or like some of us, writing generators to build the code for us).
Just in case you aren't aware (I couldn't quite tell from this context): There is a Everything that accepts Updating the docs should be easy enough.
Anything that helps make the user experience easier is definitely welcome in my book. I'd love to see an issue/pr for any of these things you've noticed!
My experience in the Fable world (pre-Feliz) was I wrote everything as just normal functions for the most part, (like one component for each .fs). The result was I lost a lot of the benefits that the React system provides. Since starting with Feliz I've had to really dive into more on how React works (a good thing imo). Since I've gotten used to it I don't really even give it much conscious thought anymore. Every single function I write that does rendering is a function component. If you really want to learn how to write React idiomatically I think it's really as simple as restricting yourself to not using Elmish, and only using function components/hooks in your demo app. You can quite easily follow along with anything written in JS out there as the API is almost exactly the same. Having a quality open-source application written in Feliz is definitely something that would be beneficial, I created an issue for it a while back #67. The closest thing right now is probably the docs app itself, or @cmeeren's Electron Demo. |
still very relevant discussion also in 2023, even more so with HTMX and the ease of Feliz.ViewEngine, also for pure HTML templating in backend, together with also the other templating approach using string templates with
i think unification in this area between Fable and Feliz view engine approaches as a single package for both backend and fronted could make sense? |
I’m a little late to the party but I agree that the IMHO, having to read the // signature needed to tell the compiler what implementation of prop.children to use
let __: _ seq -> _ = prop.children All together, I get view elements like the following: let pageLoader: ReactElement =
div [
P.className "center-xy max-xy fixed top left"
__ [
progress [ P.className "delayed circle large" ]
]
] Just my two cents… |
Hello,
this issue is for sharing my experience with Feliz and Feliz.Bulma.
This feedback is based on converting Fable Repl from Fable.React+Fulma to Feliz+Feliz.Bulma.
In the first section, I will make a listing of all the things I liked or had problems with when using Feliz. I will do something similar for Fable.React and Fulma because they are not exempt from good and less good part.
Then, in a second time, I will try to explain how went my experience with Feliz and Feliz.Bulma. The goal is to share with you how my point of view changed over time and why.
Important
I know that the subject I am analysing is sensitive and that something that afraid me. But please, remember to keep the comment section positive.
Table of content
Categorization system
I tried to find a way to organise my feedbacks, the classic "pros vs cons" felt too limited and aggressive to me.
Instead, I will be using symbols:
The same entry can have several symbols :)
Feliz
✔️ Feliz and Fable.React can be mixed because Feliz is a layer on top of Fable.React
✔️ The code is more comfortable to indent compared to Fable.React because we only have a single list. It is easier to follow the tabulation.
Click here for details
Fable.React + Fulma
Feliz + Feliz.Bulma
✔️ Thanks to the previous point it's also easy to move code around
✔️ Strongly typed API for the DOM properties
✔️ Strongly typed API for CSS and CSS units via
ICSSUnits
✔️ Doesn't pollute the context, most of the things are under
Html.*
orProp.*
✔️⚠️ Feliz offers the possibility to avoid noise in the code
Click here for details
Because there are several ways to write the same code, we can't just "read" the code. We sometimes need to take a step back to understand the outer context.
This also makes the code less "consistent" because not everything is written the same way.
I had to:
prop.text 2.0
is supported or not when I had a float.System.Func<_,_,_>
.Click here for details
Which gives this code on the calling side:
✔️⚠️ Feliz provides overloads for making user life a bit easier, but this has a cost.
For
prop.onChange
, as like 6 overloads depending on what you want to listen:static member inline onChange (handler: bool -> unit)
static member inline onChange (handler: Event -> unit)
static member inline onChange (handler: File -> unit)
static member inline onChange (handler: File list -> unit)
static member inline onChange (handler: string -> unit)
static member inline onCheckedChange (handler: bool -> unit)
This is nice because this avoids people to write "no" fun code, but the counterpart is you need to say which Event type you want explicitly.
✔️⚠️ Feliz ecosystem is mostly type-safe but doesn't prevent writing invalid code. When using Feliz and one of its extension like Feliz.Bulma you can easily mix properties but you need to be careful when doing it.
Click here for details
This code seems OK from Feliz and the F# compiler point of view but it will not give you the expected result.
text.isUppsercase
,text.isItalic
andcolor.hasTextSuccess
all output something likeClassName "my-css-class
. But in React, only the last one will have an effect so in our case the code will generate:instead of
The solution to that is to use the
++
offered by Feliz.Bulma:This is still a nice possibility because that's means you can add behaviour coming from another Feliz library to a "Feliz element" if they are compatible. People just need to be careful about it.
ℹ At first I thought Feliz didn't offer the syntax sugar
ev.Value
that we get when using Fable.React, but that's not the case.Fable.React.Extension
host the syntax sugar, so we can just open it to not pollute the context with all the Fable.React functions.ℹ The syntax doesn't reproduce the HTML way of thinking. Feliz is more a syntax sugar on top of React API than HTML. Feliz thinks in term of properties because even
children
is a property.Feliz.Bulma
✔️ Integrates well with Feliz
✔️ Easier to indent compared to Fulma
Click here for details
✔️ It's easy to identify
Bulma
components thanks toBulma
prefix✔️⚠️ The properties are accessible but pollute the context
button.*
,help.*
,columns.*
.In theory, people should only use one CSS framework, so I don't think they will have a clash on generic properties like
button.*
,columns.*
, etc.✔️ The component hierarchy seems easy enough to grabs
Bulma.card
>Bulma.cardHeader
>Bulma.cardHeaderTitle
Click here for example
Bulma.passwordInput
instead ofBulma.inputPassword
In this case, you can't easily explore the different type of input you have because they don't start with the same "prefix".
✔️⚠️ Feliz.Bulma makes it easy to mix components behaviours.
Click here for details
That's nice because as you see, it's easy to add new behaviour to an existing component. Here we added the behaviour tooltip to a button. But this also means you can write invalid code like:
instead of
Fulma is more strict about component separation and doesn't allow you to mix the behaviour unless you pass the CSS classes via
CustomClass
props.✔️⚠️ Feliz.Bulma doesn't provide a way for controlling which HTML element we want to output.
For example, we only have
Bulma.field
which generates adiv
. However, sometimes you want ap
element as the output.✔️⚠️ Feliz.Bulma is an exact mapping over Bulma
This is nice because it's slim.
But it also means:
For example, when writing a
Bulma.tabs
components you are not guided and need to know thattabs
needul
followed byli
with ana
in it.Click here to see the code
Fulma allows the user to think more in term of "Bulma components".
Note:
Fulma considers
tab
as a component and offersTabs.Tab.*
specific wrapper.Fulma still needs you to know that an
a
element is necessary, but we could add it by default (that's the case for some components).✔️⚠️ Feliz.Bulma has a smaller impact on the bundle size on a small project but will have a more significant impact when the project size increase.
Click here for details
Fulma is using a lot of DUs to model Bulma classes. You can take a look at Common.fs; also each component has its own DUs.
This requires Fulma to implements a function call
parseOptions
which in simple terms convert the DUs into classes.Feliz.Bulma takes a more direct approach by not creating a DSL on top of Bulma classes but instead directly output the classes.
Thanks to the direct class usage and the manipulation of the property list Feliz.Bulma doesn't have the big cost of all the DUs and code added in Fulma. However, it still needs an extra pass to "join" all the classes.
This part is done via Feliz.Bulma.ElementBuilders.Helpers module but all these functions are inlined. That's why the bigger your project, the bigger the impact on your code.
In Feliz.Bulma, each component holds it's own colour implementation like
button.isWarning
,help.isWarning
, etc.So if you want to add your colour, you need to implement all
button.isMyNewColor
,help.isMyNewColor
In Fulma, they all share the same color type. See the documentation
Fable.React
✔️ Follows HTML structure when you think of it as a list of properties and a list of children
Click here for a detailed explanation
Example:
As you can see just when dealing with a few cases on the property list, we have several possible syntax.
If I had to establish a consistent syntax in my project, it would be something like that:
For comparaison in Feliz, I do:
So I have only 2 cases now, and I could even write the empty version on multiple lines without too much noise if I really want a single way of structuring Feliz code.
✔️⚠️ Some of the API is typed but not all, which means that the code is not consistent.
✔️ It supports SSR
Fulma
✔️ Type-safe API
✔️ Fulma API is easy to explore via intellisense when you understand how it's structured
✔️ Fulma force you to think in term for components
✔️ Make it easy to extends the supported colour thanks to
IsCustomColor
✔️⚠️ I think it has good documentation with examples for all the components, but not every component contains the same level of information
✔️⚠️ Fulma impact on your bundle size is "stable". This point has been described in Feliz.Bulma section
Click here for an example
Props
,Modifiers
I have been working on this analysis on a period of 2-3 weeks and could still continue. But I have to end it because it's taking a lot of time and I think I now have a better view of the situation.
All that to say, that there could be more good points or problems to list especially about Fable.React and Fulma but that also the hardest part for me. I have been using them and designing them for more than 3 years so it's hard to have an objective view on them.
Was the conversion difficult?
The conversion from Fable.React to Feliz was easy enough to do. Feliz being compatible with Fable.React you can do the conversion in a progressive way file per file, component per component, etc.
To be sure I converted everything I removed all the
open Fable.React
andopen Fable.React.Props
instructions. I also removed Fulma as a dependency.Of course, I had forgotten some files so I fixed the compiler errors and then it was working.
You can see the merge request hosting the conversion here.
My journey with Feliz
When discovering Feliz at first, I was on the defensive because it was not following the "HTML/JSX way" and it's an alternate project for things I invested a lot of effort in.
Then I remembered that it's how innovation works and that what allows us to grow. For example, thanks to that that we have Elmish today.
Another thing I remembered is that JSX is not the "real React API", indeed React API is
React.createElement
. JSX is a syntax sugar on top of it, the same thing goes for Fable.React and Feliz. I insist on it because for me it helped me move forward and be curious about it.To test Feliz, I decided to use a medium size project which is the Fable REPL. Fable REPL is not a complex application but it uses a lot of feature from Fable and React.
A non-exhaustive list of things I have tested thanks to this decision:
When starting the conversion I was really pleased with how easy it was to format the code. I think the convention for writing Feliz based view could be really easy to follow. The bigger my applications grow, the more I find code formatting important.
Then in the middle, I started to be annoyed with something but couldn't find what.
I finished converting Fable REPL to Feliz, and wasn't sure if I liked or not my experience with it.
It's only now, that I trying to conclude my analysis, that I understand what holds me back. It is not much related to Feliz but more to Feliz.Bulma.
It's important to remember that for more than 2.5 years I have been working on Fulma and improving it. Fulma is one of my biggest projects. Writing code with it is great especially because everything is typed and it forces you to think in term of components.
Feliz.Bulma takes a different approach and is younger than Fulma; it's only 5 months old.
With all that said, I think it's now time to move to my conclusion :)
Conclusion
I think Feliz biggest advantage is the single list style. That's something I mentioned a lot but for me being able to format easily my code and not having to think where I should start the next line is a big plus.
Secondly, the type-safe API is a must-have. It is more verbose than the CSS equivalent but I think it's a good feature. I didn't do fancy stuff with it so I can't say if everything is supported but if that's not the case we can always make a PR to fix that :).
Feliz.Bulma shows a promising way to extends Feliz but is not yet mature enough to be a direct equivalent to Fulma. This is something I want to explore more with @Dzoukr and I hope we will be able to found a middle ground between the simplicity of Feliz.Bulma and the completeness but verbosity of Fulma.
3 years ago, the 8 Mar 2017, Tomas, Eugene and me decided to merge Fable.Arch with Elmish. This allowed us as a community to build the amazing ecosystem we have today.
I think we are currently living a similar period with Feliz and Fable.React. I think Feliz will gain more traction than Fable.React
but both will co-exist together. In the end, both of them works well together this means people can choose the one they prefer to use.
Thank you for reading my analysis and I will be happy to discuss it with you in the comment section :)
The text was updated successfully, but these errors were encountered: