Je conçois actuellement une API pour faciliter l'interaction avec React à partir de F# et la combinaison de langues offre plusieurs opportunités intéressantes pour la vérification statique. Cependant, comme prévu, il y a des bouts approximatifs et j'ai des doutes sur la façon de définir les propriétés dans un élément React DOM. Actuellement nous avons :
Option 1 : Propriétés dynamiques
module R = Fable.ReactHelper
R.input [
"type" ==> "text"
"placeholder" ==> "Your name"
"value" ==> x.state.author
"onChange" ==> x.handleAuthorChange
] []
C'est l'option la plus flexible car nous pouvons transmettre n'importe quelle propriété dans un objet JS simple. Grâce aux fonctions d'inlining, nous n'avons pas besoin d'utiliser createObj
ici et nous économisons également quelques accolades. Si vous pensez que le ==>
est trop verbeux, le raccourcissement est également trivial : let inline (=>) a b = a ==> b
Le problème est que nous n'avons pas de vérification statique ou de complétion automatique pour les propriétés. J'envisage donc d'utiliser un lambda à la place. Mais maintenant, nous devons décider d'où obtenir la signature. J'ai d'abord pris celui des IntrinsicElements définis dans le fichier de définition React :
Option 2 : Mutation des propriétés HTMLElement
R.input (fun p ->
p.``type`` <- "text"
p.placeholder <- "Your name"
p.value <- unbox x.state.author
//p.onchange <- unbox x.handleAuthorChange
p?onChange <- x.handleAuthorChange
) []
Maintenant, nous avons une vérification statique et une complétion automatique pour les propriétés, ce qui est très agréable. Notez cependant que certaines frictions avec les définitions de type commencent à apparaître et doivent être résolues avec unbox
. Le plus gros problème avec cette approche est que, bien que la définition TypeScript utilise les éléments DOM standard, certains attributs ont une orthographe différente dans React. Dans l'exemple ci-dessus, la définition de onchange
ne fonctionnera pas, nous devons donc définir onChange
manière dynamique. Cela brise presque tous les avantages de la vérification statique :(
Notre prochaine option consiste alors à utiliser React.HTMLAttributes à la place pour être sûr que nous utilisons la bonne orthographe :
Option 3 : Mutation des propriétés React.HTMLAttributes
R.input (fun p ->
p.``type`` <- Some "text"
p.placeholder <- Some "Your name"
p.value <- unbox x.state.author
p.onChange <- unbox x.handleAuthorChange
) []
Ok, toutes les propriétés ont l'orthographe comme l'attend React mais nous sommes maintenant confrontés à d'autres problèmes : d'abord les frictions avec la signature se multiplient car toutes les propriétés sont définies comme optionnelles et d'autres éléments personnalisés Typescript sont utilisés comme des types d'union effacés ou des fonctions personnalisées ; et deuxièmement, dans l'option précédente, nous avions les attributs spécifiques pour les balises input
mais ici, chaque élément HTML partage les mêmes attributs. Cependant, cela peut toujours être la meilleure option.
Il y aurait une quatrième option mais je n'y pense pas beaucoup :
Option 4 : Utilisation d'un enregistrement HTMLAttributes personnalisé
R.input (fun p ->
{ p with
``type`` = Some "text"
placeholder = Some "Your name"
value = unbox x.state.author
onChange = unbox x.handleAuthorChange
}) []
Celui-ci est probablement le plus idiomatique en F# mais les règles d'indentation deviennent plus compliquées ( { p with
ne peut pas être mis dans la première ligne) et nous n'avons pas de complétion automatique ici pour découvrir les propriétés de p
, pour autant que je sache. Plus important encore, adopter ce style nécessiterait de créer un enregistrement personnalisé pour HTMLAttributes
car ceux de TypeScript sont analysés comme des interfaces (il y a de bonnes raisons à cela), donc cela ajouterait une étape supplémentaire dans la maintenance de la Fable Fichier
Voici les options pour prendre la décision et j'aimerais entendre votre avis à ce sujet. Je sais qu'il est possible d'ajouter plusieurs options à l'API, mais cela le rendrait probablement plus déroutant pour les nouveaux arrivants et je préférerais faire simple au début. Merci beaucoup pour votre aide d'avance !
Option 5.
Étendre l'option 1. pour ajouter des attributs typés. Je faisais une API similaire pour FunScript à l'époque.
https://github.com/FractalProject/Fractal.Sample/blob/master/client/App.fsx#L65 -L74
https://github.com/FractalProject/Fractal/blob/master/src/Fractal.DOM.fs
Option 6.
Devenez super fou et créez une expression de calcul avec des mots-clés personnalisés.
Pour les nouveaux arrivants, je pense que l'option 4 semble être la meilleure, pas non plus de cordes magiques et elle garde les choses immuables.
Merci pour la suggestion, @Krzysztof-Cieslak ! Au début, j'avais peur que cela nécessite plus de travaux d'entretien, mais après réflexion, j'ai réalisé que c'était la meilleure option. J'ai construit sur votre code et écrit un script pour générer (partiellement) le module d'assistance :
https://github.com/fsprojects/Fable/blob/master/samples/browser/react/public/Fable.ReactHelper.fs
Le code de rendu est beaucoup plus joli maintenant avec une vérification statique complète :)
https://github.com/fsprojects/Fable/blob/master/samples/browser/react/public/components.fs#L104 -L124
Il y a encore du travail sur React Helper, mais je ferme le problème pour le moment. Merci à tous pour votre aide!
Commentaire le plus utile
Option 5.
Étendre l'option 1. pour ajouter des attributs typés. Je faisais une API similaire pour FunScript à l'époque.
https://github.com/FractalProject/Fractal.Sample/blob/master/client/App.fsx#L65 -L74
https://github.com/FractalProject/Fractal/blob/master/src/Fractal.DOM.fs
Option 6.
Devenez super fou et créez une expression de calcul avec des mots-clés personnalisés.