Feliz: Ihre Meinung zur v2-Idee?

Erstellt am 17. März 2021  ·  9Kommentare  ·  Quelle: Zaid-Ajaj/Feliz

Hallo Freund,
Als täglicher Benutzer von Feliz (und Feliz.Bulma tatsächlich) habe ich immer Probleme mit der Verschachtelung von prop.children , was es für mich irgendwie hässlicher macht. Ich würde gerne Ihre Meinung zum v2-Vorschlag wissen, wo wir Folgendes hätten:

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

Vielleicht , dass so etwas ist nicht einmal möglich, aber ich sah auf Feliz.Engine @alfonsogarciacaro seiner Arbeit , und es scheint so verlockend , Requisiten und Kinder auf dem gleichen Niveau zu haben. 😄

discussion

Hilfreichster Kommentar

Petition, den Titel zu ändern, um das Diskussionsthema anzugeben? 😆

@Dzoukr Es gab frühere Diskussionen darüber - ich bin definitiv auf Ihrer Seite.

Hilfsfunktionen sind nicht ideal, weil sie die umständliche Doppellistensyntax zurückbringen, was im Grunde einen der Hauptvorteile dieser Bibliothek von vornherein eliminiert, und sie helfen nicht bei der echten Verwendung von Props, die viel mehr ist als nur div+class.

@Zaid-Ajaj Wären Sie offen für einen alternativen Namespace, um dieses Gebiet zu erkunden? Wie Feliz.Experimental oder so? Der Hauptnamespace von Feliz verfolgt also das Ziel einer dünnen Schicht über React, während der andere einen eigenwilligeren Ansatz versuchen kann.

Wie auch immer, ich mag so etwas, um die Requisitengruppierung aufrechtzuerhalten:

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

Die Verschachtelung von Requisiten scheint sauberer zu sein als die Verschachtelung von Elementen, da Requisiten nicht unendlich verschachtelt werden können, wie dies bei Elementen der Fall ist. Der props Container würde eine Schnittstelle mit den Html.* Elementen teilen und beide als gültige untergeordnete Elemente des enthaltenden Elements modellieren.

Die Leistung wäre etwas, das man im Auge behalten sollte, aber ich glaube nicht, dass die Auswirkungen bekannt sind, ohne es zu versuchen. Wer weiß, vielleicht klingt der Mehraufwand nur beängstigend, stellt sich aber in der Praxis im Vergleich zu allem anderen beim Rendern als unbedeutend heraus.

Alle 9 Kommentare

Das Hinzufügen dieser Art von Unterstützung macht die Typinferenz viel freizügiger (ich würde sogar sagen nutzlos), weil wir dann Attribute = Properties = ReactElement . Grundsätzlich wird alles als der gleiche Typ akzeptiert...

Um dies zu ermöglichen, müssen wir außerdem eine zusätzliche Ebene über React hinzufügen, um die Attribute von den Kindern "manuell" zu trennen, was sich auf die Leistung auswirkt, da immer zusätzliche Schritte erforderlich sind. Dadurch wird auch die Bündelgröße erhöht, um die für die Trennung erforderlichen Informationen zu speichern.

Ich persönlich bin nicht für diese Funktion.

Ich habe gemischte Gefühle bei diesem Ansatz, hauptsächlich wegen des Laufzeit-Overheads, den er zum Rendern hinzufügt. Außerdem würde ich es vorziehen, die Idee eines ReactElement von ReactAttribute . Daher bevorzuge ich persönlich die aktuelle API viel mehr.

Nach meiner Erfahrung höre ich von Benutzern viel über dieses Problem, aber _nur_ im Kontext von div mit einem Klassennamen und Kindern. Worauf meine übliche Antwort darin besteht, eine kleine Hilfsfunktion zu schreiben, mit der Sie so etwas oder Ähnliches schreiben können

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

Mir scheint, es gibt keinen Grund, nicht Ihre eigenen Erweiterungsmethoden zu schreiben, wenn sie besser zum Effekt der Anwendung von Klassen oder anderen häufig verwendeten Eigenschaften passen, wie ich es hier mit Material UI getan habe. Die Composability ist mit F# kinderleicht, ohne den Compiler weiter zu verwirren oder die Typsicherheit zu reduzieren.

[<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
        ]

Danke für alle Meinungen, ich wollte nur die Diskussion fließen lassen. 👍 Ich habe auch meine eigenen Helfer wie divClassed "myClass" [ children ] oder so, wollte aber die Sicht anderer wissen.

Dies sollte unter Beibehaltung der Typbeschränkungen tatsächlich möglich sein, wenn wir React-Elemente von einer Liste in Berechnungsausdrücke geändert haben:

div {
    className "myClass"

    div { ... }
}

Die CE-Implementierung würde die Codebasis vergrößern, die Wartung definitiv erschweren, und der Umgang mit einigen Dingen wie Primitiven, wenn Requisiten definiert werden, ist ein wenig knifflig / müsste beim Kompilieren fehlschlagen, anstatt während der Entwicklung, wie es jetzt der Fall ist:

// 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()

Was ich mir nicht sicher bin, ist, wie man Fable dazu bringt, alle / alle CE-Installationen richtig zu kompilieren und die Operationen auffindbar zu machen (wie es derzeit beim Typ prop Fall ist).

Petition, den Titel zu ändern, um das Diskussionsthema anzugeben? 😆

@Dzoukr Es gab frühere Diskussionen darüber - ich bin definitiv auf Ihrer Seite.

Hilfsfunktionen sind nicht ideal, weil sie die umständliche Doppellistensyntax zurückbringen, was im Grunde einen der Hauptvorteile dieser Bibliothek von vornherein eliminiert, und sie helfen nicht bei der echten Verwendung von Props, die viel mehr ist als nur div+class.

@Zaid-Ajaj Wären Sie offen für einen alternativen Namespace, um dieses Gebiet zu erkunden? Wie Feliz.Experimental oder so? Der Hauptnamespace von Feliz verfolgt also das Ziel einer dünnen Schicht über React, während der andere einen eigenwilligeren Ansatz versuchen kann.

Wie auch immer, ich mag so etwas, um die Requisitengruppierung aufrechtzuerhalten:

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

Die Verschachtelung von Requisiten scheint sauberer zu sein als die Verschachtelung von Elementen, da Requisiten nicht unendlich verschachtelt werden können, wie dies bei Elementen der Fall ist. Der props Container würde eine Schnittstelle mit den Html.* Elementen teilen und beide als gültige untergeordnete Elemente des enthaltenden Elements modellieren.

Die Leistung wäre etwas, das man im Auge behalten sollte, aber ich glaube nicht, dass die Auswirkungen bekannt sind, ohne es zu versuchen. Wer weiß, vielleicht klingt der Mehraufwand nur beängstigend, stellt sich aber in der Praxis im Vergleich zu allem anderen beim Rendern als unbedeutend heraus.

@zanaptak Ich mag diese Idee sehr, denn wie Sie sagen, neigen die Requisiten nicht dazu, sich tief zu verschachteln wie die Kinder.


Zum Thema Helfer und die Doppellistensyntax

Sie helfen nicht bei der echten Prop-Nutzung, die viel mehr ist als nur div+class

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"
]

Sie könnten sogar einen Operator definieren, um die "doppelte Liste"-ähnliche Syntax mit jedem Element zu verwenden, wenn Sie möchten:

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" ]

Dies ist ein heikles Thema, denn was "besser aussieht" kann subjektiv sein. Das Wichtigste ist wahrscheinlich, Breaking Changes zu vermeiden, aber wenn Sie nur experimentieren möchten, können Sie die Feliz.Engine dafür verwenden, da sie sehr einfach an React

// 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))

Prüfen:

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

let myElAsReactEl = myEl.AsReactEl // ReactElement 

Hilfsfunktionen sind nicht ideal, da sie die umständliche Doppellistensyntax zurückbringen.

@zanaptak Helper-Funktionen brauchen nicht zwei Listen als Eingabe, div [ "class" ] [ ] war nur ein Beispiel für ein gängiges Szenario, das zufällig zwei Listen benötigt. Diese Funktionen können auch Komponenten sein, die Ihre Sicht vereinfachen

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

Obwohl Sie die Listen technisch unendlich verschachteln können, ist dies nicht die Idee. Es gibt ein Gleichgewicht, indem Sie kleinere Funktionen oder Komponenten in Ihrem größeren UI-Code verwenden.

Im Idealfall müssten Sie den Code jedoch nicht so oft aufschlüsseln, und die eine Funktion sollte so gut wie möglich lesbar sein, da das Denken an die Organisation der Dinge manchmal den _Flow_ nimmt. Dies war das Problem mit dem DSL von Fable.React, wo man jedes Mal _denken_ musste, wie man Dinge formatiert. Feliz behebt dies auf Kosten von zwei Verschachtelungsebenen von children .

Ehrlich gesagt weiß ich nicht, was die Wunderwaffe ist. Wie @alfonsogarciacaro sagte

Das ist ein heikles Thema, denn was "besser aussieht" kann subjektiv sein

Zum Thema Experimente:

Wären Sie offen für einen alternativen Namespace, um diesen Bereich zu erkunden? Wie Feliz.Experimental oder so? Der Hauptnamespace von Feliz verfolgt also das Ziel einer dünnen Schicht über React, während der andere einen eigenwilligeren Ansatz versuchen kann.

Zum jetzigen Zeitpunkt gibt es keine Pläne, verschiedene DSLs im Feliz-Projekt zu unterhalten. Ich bin in Bezug auf OSS-Zeit/-Kapazität sehr begrenzt und würde hoffen, Anstrengungen zur Verbesserung des aktuellen Ansatzes zu unternehmen: Dokumentation, Beispiele und besser getestete Ökosystembibliotheken.

Experimentieren Sie jedoch gerne mit Feliz.Engine von

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

Zaid-Ajaj picture Zaid-Ajaj  ·  8Kommentare

alfonsogarciacaro picture alfonsogarciacaro  ·  6Kommentare

Zaid-Ajaj picture Zaid-Ajaj  ·  6Kommentare

Dzoukr picture Dzoukr  ·  9Kommentare

cmeeren picture cmeeren  ·  13Kommentare