Feliz: Votre avis sur l'idée de la v2 ?

Créé le 17 mars 2021  ·  9Commentaires  ·  Source: Zaid-Ajaj/Feliz

Salut l'ami,
en tant qu'utilisateur quotidien de Feliz (et de Feliz.Bulma en fait), j'ai toujours du mal à imbriquer prop.children ce qui le rend en quelque sorte plus moche pour moi. J'aimerais connaître votre avis sur la proposition v2 où nous aurions :

Html.div [
    prop.className "myClass"
    Html.div "Nested"
]

Peut - être quelque chose comme ça ne peut même pas, mais je l' ai vu le travail de @alfonsogarciacaro sur Feliz.Engine et il semble si tentant d'avoir des accessoires et des enfants sur le même niveau. ??

discussion

Commentaire le plus utile

Pétition pour changer le titre pour indiquer le sujet de discussion ? ??

@Dzoukr Il y a eu des discussions précédentes à ce sujet -- je suis définitivement de votre côté.

Les fonctions d'assistance ne sont pas idéales car elles ramènent la syntaxe gênante de la double liste, éliminant essentiellement l'un des principaux avantages de cette bibliothèque en premier lieu, et elles n'aident pas à l'utilisation réelle des accessoires, qui est bien plus qu'un simple div+class.

@Zaid-Ajaj Seriez-vous ouvert à un autre espace de noms pour explorer ce domaine ? Comme Feliz.Experimental ou quelque chose comme ça ? Ainsi, l'espace de noms principal de Feliz garde l'objectif de la couche mince sur React tandis que l'autre peut essayer une approche plus avisée.

Quoi qu'il en soit, j'aime quelque chose comme ça pour maintenir le regroupement des props :

Html.div [
    props [
        prop.className "myClass"
    ]
    Html.div "Nested"
]

L'imbrication d'accessoires semble plus propre que l'imbrication d'éléments, car les accessoires ne sont pas soumis à une imbrication infinie comme le sont les éléments. Le conteneur props partagerait une interface avec les éléments Html.* , modélisant les deux comme des enfants valides de l'élément conteneur.

La performance serait quelque chose à surveiller, mais je ne pense pas que l'impact soit connu sans essayer. Qui sait, peut-être que le travail supplémentaire semble effrayant mais s'avère insignifiant dans la pratique par rapport à tout le reste qui se passe lors du rendu.

Tous les 9 commentaires

L'ajout de ce type de support rendra l'inférence de type beaucoup plus permissive (je dirais même inutile) car nous aurons alors Attribute = Properties = ReactElement . En gros, tout sera accepté comme du même type...

De plus, afin de rendre cela possible, nous devrons ajouter une couche supplémentaire au-dessus de React pour séparer les attributs des enfants "manuellement", ce qui aura un impact sur les performances car nous aurons toujours des étapes supplémentaires nécessaires. Cela augmentera également la taille du paquet afin de stocker les informations nécessaires à la séparation.

Personnellement, je ne suis pas pour cette fonctionnalité.

J'ai des sentiments mitigés à propos de cette approche, principalement à cause de la surcharge d'exécution qu'elle ajoute au rendu. Aussi, je préférerais garder l'idée d'un ReactElement séparé de ReactAttribute . Donc personnellement, je préfère beaucoup plus l'API actuelle.

D'après mon expérience, j'entends beaucoup parler de ce problème de la part des utilisateurs, mais _uniquement_ dans le contexte de div avec un nom de classe et des enfants. À laquelle ma réponse habituelle est d'écrire une petite fonction d'aide qui vous permet d'écrire quelque chose comme ceci ou similaire

div [ "myClass" ] [
  Html.div "nested"
]

Il me semble qu'il n'y a aucune raison de ne pas écrire vos propres méthodes d'extension si elles conviennent mieux à l'effet de l'application de classes ou d'autres propriétés fréquemment utilisées comme je l'ai fait ici avec Material UI. La composabilité est très simple avec F# sans perturber davantage le compilateur ni réduire la sécurité des types.

[<AutoOpen>]
module MuiExtensions

open Feliz
open Feliz.MaterialUI

type Mui 
    with
    static member gridItem (children:seq<ReactElement>):ReactElement = 
        Mui.grid [
            grid.item true
            grid.children children
        ]

    static member gridItem (props:seq<IReactProperty>):ReactElement = 
        Mui.grid [
            grid.item true
            yield! props
        ]

Merci pour tous les avis, je voulais juste faire circuler la discussion. 👍 J'ai aussi mes propres assistants comme divClassed "myClass" [ children ] environ, mais je voulais connaître le point de vue des autres.

Cela devrait en fait être possible tout en conservant les restrictions de type si nous modifions les éléments React d'une liste à des expressions de calcul :

div {
    className "myClass"

    div { ... }
}

L'implémentation de CE augmenterait la taille de la base de code, la rendrait certainement plus difficile à maintenir, et la gestion de certaines choses comme les primitives lorsque les accessoires sont définis est un peu délicate/devrait échouer à la compilation plutôt que pendant le développement comme c'est le cas maintenant :

// Would probably need to make the base builder generic so we can use a type restriction and define this DU for
// each element so we can properly restrict what types are valid as primatives
type ReactBuilderChild =
    | Child of ReactElement
    | Children of ReactElement list
    | Float of float
    | Int of int
    | String of string
    | None

    member this.GetChildren () =
        match this with
        | ReactBuilderChild.Children children -> children
        | _ -> failwith "Cannot combine children with primitive values."

type ReactBuilderState =
    { Children: ReactElement list
      Props: IReactProperty list } 

[<AbstractClass;NoEquality;NoComparison>]
type ReactBuilder () =
    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member _.Yield (x: ReactElement) = { Children = ReactBuilderChild.Child x; Props = [] }
    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member _.Yield (x: ReactElement list) = { Children = ReactBuilderChild.Children x; Props = [] }
    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member _.Yield (x: float) = { Children = ReactBuilderChild.Float x; Props = [] }
    ...
    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member _.Yield (x: unit) = { Children = ReactBuilderChild.None; Props = [] }

    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member _.Combine (state: ReactBuilderState, x: ReactBuilderState) = 
        { Children = (state.Children.GetChildren()) @ (x.Children.GetChildren()); Props = state.Props @ x.Props }

    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member _.Zero () = { Children = ReactBuilderChild.None; Props = [] }

    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member this.For (state: ReactBuilderState, f: unit -> ReactBuilderState) = this.Combine(state, f())

    [<CustomOperation("className")>]
    member _.className (state: ReactBuilderState, value: string) =
        { state with Props = (Interop.mkAttr "className" value)::state.Props }

    [<CustomOperation("children")>]
    member _.children (state: ReactBuilderState, value: ReactElement list) =
        { state with Children = state.Children @ value }

    abstract Run : ReactBuilderState -> ReactElement

[<NoEquality;NoComparison>]
type DivBuilder () =
    inherit ReactBuilder()

    [<EditorBrowsable(EditorBrowsableState.Never)>]
    member _.Run (state: ReactBuilderState) = 
        match state with
        | { Children = ReactBuilderChild.None; Props = props } -> Interop.createElement "div" props
        | { Children = ReactBuilderChild.Children children } -> Interop.reactElementWithChildren "div" children
        | { Children = ReactBuilderChild.Float f } -> Interop.reactElementWithChild "div" f
        | _ -> ...

let div = DivBuilder()

Ce dont je ne suis pas sûr, c'est de savoir comment faire en sorte que Fable compile correctement tout ou partie de la plomberie CE, ainsi que de rendre les opérations détectables (comme c'est actuellement le cas avec le type prop ).

Pétition pour changer le titre pour indiquer le sujet de discussion ? ??

@Dzoukr Il y a eu des discussions précédentes à ce sujet -- je suis définitivement de votre côté.

Les fonctions d'assistance ne sont pas idéales car elles ramènent la syntaxe gênante de la double liste, éliminant essentiellement l'un des principaux avantages de cette bibliothèque en premier lieu, et elles n'aident pas à l'utilisation réelle des accessoires, qui est bien plus qu'un simple div+class.

@Zaid-Ajaj Seriez-vous ouvert à un autre espace de noms pour explorer ce domaine ? Comme Feliz.Experimental ou quelque chose comme ça ? Ainsi, l'espace de noms principal de Feliz garde l'objectif de la couche mince sur React tandis que l'autre peut essayer une approche plus avisée.

Quoi qu'il en soit, j'aime quelque chose comme ça pour maintenir le regroupement des props :

Html.div [
    props [
        prop.className "myClass"
    ]
    Html.div "Nested"
]

L'imbrication d'accessoires semble plus propre que l'imbrication d'éléments, car les accessoires ne sont pas soumis à une imbrication infinie comme le sont les éléments. Le conteneur props partagerait une interface avec les éléments Html.* , modélisant les deux comme des enfants valides de l'élément conteneur.

La performance serait quelque chose à surveiller, mais je ne pense pas que l'impact soit connu sans essayer. Qui sait, peut-être que le travail supplémentaire semble effrayant mais s'avère insignifiant dans la pratique par rapport à tout le reste qui se passe lors du rendu.

@zanaptak J'aime assez cette idée car comme vous le dites, les accessoires n'ont pas tendance à s'emboîter profondément comme les enfants.


Sur le thème des helpers et de la syntaxe de la double liste

ils n'aident pas à l'utilisation réelle des accessoires, qui est bien plus qu'un simple div + classe

let inline withProps elementFunction props (children: #seq<ReactElement>) =
    (prop.children (children :> ReactElement seq))
    :: props
    |> elementFunction

let myDiv = withProps Html.div

myDiv [ prop.className "myClass" ] [
    Html.div "Nested"
]

Vous pouvez même définir un opérateur pour utiliser la syntaxe de type "double liste" avec n'importe quel élément si vous le souhaitez :

let inline (@+) elementFunction props =
    fun children -> withProps elementFunction props children

let inline (@++) elementFunction prop =
    fun children -> withProps elementFunction [ prop ] children

Html.div @+ [ prop.className "myClass" ]
<| [ Html.div "nested" ]

Html.div @++ prop.className "myClass"
<| [ Html.div "nested" ]

C'est un sujet délicat car ce qui "semble mieux" peut être subjectif. La chose la plus importante est probablement d'éviter de casser les changements, mais si vous voulez juste expérimenter, vous pouvez utiliser Feliz.Engine pour cela, car il est très facile de l'adapter à React. Comme mentionné dans d'autres numéros, je ne souhaite pas publier Feliz.Engine.React car il serait déroutant d'avoir deux packages "concurrents" similaires. Mais par exemple, si vous voulez tout avoir au même niveau, vous avez juste besoin d'un module comme celui-ci (il serait facile de l'adapter pour avoir des accessoires dans une liste séparée aussi) :

// Dependencies:
// - Fable.React
// - Feliz.Engine.Event dependencies

open Fable.Core
open Fable.Core.JsInterop
open Feliz
open Fable.React

type Node =
    | El of ReactElement
    | Style of string * string
    | Prop of string * obj
    member this.AsReactEl =
        match this with
        | El el -> el
        | _ -> failwith "not an element"

let private makeNode tag nodes =
    let props = obj()
    let style = obj()
    let children = ResizeArray()
    nodes |> Seq.iter (function
        | El el -> children.Add(el)
        | Style(k, v) -> style?(k) <- v
        | Prop(k, v) -> props?(k) <- v
    )
    if JS.Constructors.Object.keys(style).Count > 0 then
        props?style <- style
    ReactBindings.React.createElement(tag, props, children) |> El

let Html = HtmlEngine(makeNode, str >> El, fun () -> El nothing)

let Svg = SvgEngine(makeNode, str >> El, fun () -> El nothing)

let prop = AttrEngine((fun k v -> Prop(k, v)), (fun k v -> Prop(k, v)))

let style = CssEngine(fun k v -> Style(k, v))

let ev = EventEngine(fun k f -> Prop("on" + k.[0].ToString().ToUpper() + k.[1..], f))

Test:

let myEl = // Node
    Html.div [
        prop.className "myClass"
        Html.div "Nested"
    ]

let myElAsReactEl = myEl.AsReactEl // ReactElement 

Les fonctions d'assistance ne sont pas idéales car elles ramènent la syntaxe gênante de la double liste.

Les fonctions d'assistance de div [ "class" ] [ ] n'était qu'un exemple de scénario courant prenant deux listes. Ces fonctions peuvent également être des composants qui simplifient votre vue

Todo.List [ for item in items -> Todo.Item item ]

Bien que vous puissiez techniquement imbriquer les listes à l'infini, ce n'est pas l'idée. Il y a un équilibre en utilisant des fonctions ou des composants plus petits dans votre plus grand code d'interface utilisateur.

Cependant, idéalement, vous n'auriez pas besoin de décomposer le code aussi souvent et la fonction unique devrait être aussi lisible que possible, car penser à organiser les choses enlève parfois le _flow_. C'était le problème avec le DSL de Fable.React où il fallait _penser_ à la façon de formater les choses à chaque fois. Feliz corrige cela au prix de deux niveaux d'imbrication de children .

Pour être honnête, je ne sais pas quelle est la solution miracle. Comme l'a dit @alfonsogarciacaro

C'est un sujet délicat car ce qui "semble mieux" peut être subjectif

Au sujet des expérimentations :

Seriez-vous ouvert à un autre espace de noms pour explorer ce domaine ? Comme Feliz.Experimental ou quelque chose comme ça ? Ainsi, l'espace de noms principal de Feliz garde l'objectif de la couche mince sur React tandis que l'autre peut essayer une approche plus avisée.

À l'heure actuelle, il n'est pas prévu de maintenir différentes DSL dans le projet Feliz. Je suis très limité en temps/capacité OSS et j'espère orienter mes efforts vers l'amélioration de l'approche actuelle : documentation, échantillons et bibliothèques écosystémiques plus bien testées.

Cela dit, n'hésitez pas à expérimenter avec Feliz.Engine de @alfonsogarciacaro et utilisez le DSL qui vous convient le mieux.

Cette page vous a été utile?
0 / 5 - 0 notes