Feliz: Sua opinião sobre a ideia v2?

Criado em 17 mar. 2021  ·  9Comentários  ·  Fonte: Zaid-Ajaj/Feliz

Olá amiga,
como um usuário diário de Feliz (e Feliz.Bulma de fato), estou sempre lutando para aninhar prop.children que o torna um pouco mais feio para mim. Gostaria de saber sua opinião sobre a proposta v2 em que teríamos:

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

Talvez algo como isso não é mesmo possível, mas eu vi 's trabalho @alfonsogarciacaro em Feliz.Engine e parece tão tentador ter adereços e crianças no mesmo nível. 😄

discussion

Comentários muito úteis

Petição para mudar o título para indicar o tópico de discussão? 😆

@Dzoukr Houve discussões anteriores sobre isso - estou definitivamente do seu lado.

As funções auxiliares não são ideais porque trazem de volta a estranha sintaxe de lista dupla, basicamente eliminando um dos principais benefícios desta lib em primeiro lugar, e não ajudam com o uso real de prop, que é muito mais do que apenas div + class.

@ Zaid-Ajaj Você estaria aberto a um namespace alternativo para explorar esta área? Como Feliz.Experimental ou algo assim? Assim, o namespace do Feliz principal mantém o objetivo de camada fina sobre o React, enquanto o alternativo pode tentar uma abordagem mais opinativa.

De qualquer forma, gosto de algo assim para manter o agrupamento de acessórios:

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

O aninhamento de adereços parece mais limpo do que o aninhamento de elementos, pois os adereços não estão sujeitos a aninhamento infinito da maneira que os elementos estão. O contêiner props compartilharia uma interface com os elementos Html.* , modelando ambos como filhos válidos do elemento que o contém.

O desempenho seria algo para ficar de olho, mas não acho que o impacto seja conhecido sem tentar. Quem sabe, talvez o trabalho extra soe assustador, mas acabe sendo insignificante na prática em relação a tudo o mais que acontece na renderização.

Todos 9 comentários

Adicionar este tipo de suporte tornará a inferência de tipo muito mais permissiva (eu diria até mesmo inútil) porque então teremos Attribute = Properties = ReactElement . Basicamente, tudo será aceito como do mesmo tipo ...

Além disso, para tornar isso possível, precisaremos adicionar uma camada extra no topo do React para dividir os atributos dos filhos "manualmente", o que afetará o desempenho, pois sempre teremos etapas extras necessárias. Isso também aumentará o tamanho do pacote para armazenar as informações necessárias para fazer a sepração.

Pessoalmente, não sou a favor desse recurso.

Tenho sentimentos confusos sobre essa abordagem, principalmente por causa da sobrecarga de tempo de execução que ela adiciona à renderização. Também prefiro manter a ideia de ReactElement separada de ReactAttribute . Então, pessoalmente, prefiro muito mais a API atual.

Em minha experiência, ouço muito sobre esse problema de usuários, mas _somente_ no contexto de div com um nome de classe e filhos. Para a qual minha resposta usual é escrever uma pequena função auxiliar que permite que você escreva algo assim ou semelhante

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

Parece-me que não há razão para não escrever seus próprios métodos de extensão se eles se adequarem melhor ao efeito da aplicação de classes ou outras propriedades usadas com frequência, como fiz aqui com Material UI. A composibilidade é extremamente fácil com F # sem confundir ainda mais o compilador ou reduzir a segurança de tipo.

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

Obrigado por todas as opiniões, eu só queria fluir a discussão. 👍 Eu também tenho meus próprios ajudantes, como divClassed "myClass" [ children ] ou algo assim, mas queria saber o ponto de vista dos outros.

Na verdade, isso deve ser possível, mantendo as restrições de tipo, se alterarmos os elementos React de uma lista para expressões de computação:

div {
    className "myClass"

    div { ... }
}

A implementação do CE aumentaria o tamanho da base de código, definitivamente tornaria mais difícil mantê-la, e lidar com algumas coisas como primitivas quando props são definidas é um pouco complicado / teria que falhar na compilação em vez de durante o desenvolvimento, como acontece agora:

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

O que não tenho certeza é como fazer o Fable compilar adequadamente qualquer / todos os encanamentos CE, bem como tornar as operações detectáveis ​​(como é atualmente com o tipo prop ).

Petição para mudar o título para indicar o tópico de discussão? 😆

@Dzoukr Houve discussões anteriores sobre isso - estou definitivamente do seu lado.

As funções auxiliares não são ideais porque trazem de volta a estranha sintaxe de lista dupla, basicamente eliminando um dos principais benefícios desta lib em primeiro lugar, e não ajudam com o uso real de prop, que é muito mais do que apenas div + class.

@ Zaid-Ajaj Você estaria aberto a um namespace alternativo para explorar esta área? Como Feliz.Experimental ou algo assim? Assim, o namespace do Feliz principal mantém o objetivo de camada fina sobre o React, enquanto o alternativo pode tentar uma abordagem mais opinativa.

De qualquer forma, gosto de algo assim para manter o agrupamento de acessórios:

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

O aninhamento de adereços parece mais limpo do que o aninhamento de elementos, pois os adereços não estão sujeitos a aninhamento infinito da maneira que os elementos estão. O contêiner props compartilharia uma interface com os elementos Html.* , modelando ambos como filhos válidos do elemento que o contém.

O desempenho seria algo para ficar de olho, mas não acho que o impacto seja conhecido sem tentar. Quem sabe, talvez o trabalho extra soe assustador, mas acabe sendo insignificante na prática em relação a tudo o mais que acontece na renderização.

@zanaptak Eu gosto bastante dessa ideia porque, como você disse, os adereços não tendem a se aninhar profundamente como as crianças.


Sobre o tópico de ajudantes e a sintaxe de lista dupla

eles não ajudam com o uso real de prop, que é muito mais do que apenas 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"
]

Você pode até definir um operador para usar a sintaxe do tipo "lista dupla" com qualquer elemento, se desejar:

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

Este é um tópico complicado porque o que "parece melhor" pode ser subjetivo. Provavelmente o mais importante é evitar que as alterações sejam interrompidas, mas se você quer apenas experimentar pode usar o Feliz.Engine para isso, pois é muito fácil adaptá-lo ao React. Conforme mencionado em outras edições, não desejo publicar Feliz.Engine.React porque seria confuso ter dois pacotes "concorrentes" semelhantes. Mas, por exemplo, se você quiser ter tudo no mesmo nível, você só precisa de um módulo como este (seria fácil adaptá-lo para ter adereços em uma lista separada também):

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

Teste:

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

let myElAsReactEl = myEl.AsReactEl // ReactElement 

As funções auxiliares não são ideais porque trazem de volta a estranha sintaxe de lista dupla.

As funções auxiliares do div [ "class" ] [ ] foi apenas um exemplo de um cenário comum que aconteceu para receber duas listas. Essas funções também podem ser componentes que tornam sua visualização mais simples

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

Embora você possa tecnicamente aninhar as listas infinitamente, essa não é a ideia. Há um equilíbrio aí usando funções ou componentes menores em seu código de IU maior.

No entanto, o ideal é que você não precise quebrar o código com tanta frequência e uma função deve ser o mais legível possível, porque pensar em organizar as coisas às vezes tira o _fluxo_. Esse era o problema com o DSL da Fábula. Reaja onde você teve que _pensar_ sobre como formatar as coisas todas as vezes. Feliz corrige isso ao custo de dois níveis de aninhamento de children .

Para ser honesto, não sei o que é a bala de prata. Como @alfonsogarciacaro disse

Este é um tópico complicado porque o que "parece melhor" pode ser subjetivo

Sobre o tema dos experimentos:

Você estaria aberto a um namespace alternativo para explorar esta área? Como Feliz.Experimental ou algo assim? Assim, o namespace do Feliz principal mantém o objetivo de camada fina sobre o React, enquanto o alternativo pode tentar uma abordagem mais opinativa.

No momento, não há planos de manter DSLs diferentes no projeto Feliz. Estou muito limitado no tempo / capacidade do OSS e espero direcionar esforços para melhorar a abordagem atual: documentação, amostras e bibliotecas de ecossistema mais bem testadas.

Dito isso, sinta-se à vontade para experimentar o Feliz.Engine da @alfonsogarciacaro e usar o DSL que mais lhe

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

alfonsogarciacaro picture alfonsogarciacaro  ·  6Comentários

l3m picture l3m  ·  10Comentários

Dzoukr picture Dzoukr  ·  9Comentários

Zaid-Ajaj picture Zaid-Ajaj  ·  8Comentários

mastoj picture mastoj  ·  3Comentários