Feliz: v2のアイデアについてのあなたの意見は?

作成日 2021年03月17日  ·  9コメント  ·  ソース: Zaid-Ajaj/Feliz

こんにちは友人、
Feliz(そして実際にはFeliz.Bulma)の毎日のユーザーとして、私は常にprop.childrenネストに苦労しているため、どういうわけか醜いものになっています。 私たちが持っているであろうv2提案についてのあなたの意見を知りたいです:

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

そのようなことは不可能かもしれませんが、 @ alfonsogarciacaroのFeliz.Engineでの作業を見て、同じレベルの小道具と子供がいるのはとても魅力的です。 😄

discussion

最も参考になるコメント

ディスカッショントピックを示すためにタイトルを変更する請願? 😆

@Dzoukrこれについては以前に議論がありました-私は間違いなくあなたの味方です。

ヘルパー関数は、厄介なダブルリスト構文を復活させ、基本的にこのlibの主な利点の1つを最初から排除し、div + class以上の実際のpropの使用には役立たないため理想的ではありません。

@ Zaid-Ajajこの領域を探索するために、別の名前空間を利用できますか? Feliz.Experimentalか何かのように? したがって、メインのFeliz名前空間は、Reactよりも薄い層の目標を維持しますが、代替はより意見の分かれるアプローチを試すことができます。

とにかく、私は小道具のグループ化を維持するためにこのようなものが好きです:

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

小道具は要素のように無限に入れ子になることがないため、小道具の入れ子は要素の入れ子よりもきれいに見えます。 propsコンテナは、 Html.*要素とインターフェイスを共有し、両方を含む要素の有効な子としてモデル化します。

パフォーマンスは注目に値するものですが、試してみないとその影響はわからないと思います。 誰が知っているか、多分余分な仕事は恐ろしいように聞こえるだけですが、レンダリングで起こっている他のすべてに比べて実際には取るに足らないことがわかります。

全てのコメント9件

この種のサポートを追加すると、 Attribute = Properties = ReactElementが得られるため、型推論がはるかに寛容になります(私は役に立たないとさえ言えます)。 基本的に、すべてが同じタイプとして受け入れられます...

また、それを可能にするために、Reactの上に追加のレイヤーを追加して、子から属性を「手動で」分割する必要があります。これは、常に追加の手順が必要になるため、パフォーマンスに影響を与えます。 これにより、分離を行うために必要な情報を保存するために、バンドルサイズも大きくなります。

個人的には、私はこの機能には向いていません。

主にレンダリングに追加される実行時のオーバーヘッドのために、私はこのアプローチについて複雑な気持ちを持っています。 また、私はのアイデアを保つことを好むだろうReactElementとは別にReactAttribute 。 個人的には、現在のAPIの方がはるかに好きです。

私の経験では、この問題についてユーザーからよく耳にしますが、クラス名と子を持つdivのコンテキストでのみ_のみ_です。 私の通常の応答は、このようなものを書くことができる小さなヘルパー関数を書くことです

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

ここでMaterialUIを使用して行ったように、クラスやその他の頻繁に使用されるプロパティを適用する効果に適している場合は、独自の拡張メソッドを作成しない理由はないようです。 F#を使用すると、コンパイラをさらに混乱させたり、型の安全性を低下させたりすることなく、構成が非常に簡単になります。

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

たくさんのご意見ありがとうございました。流れについて話し合いたいと思いました。 👍私にもdivClassed "myClass" [ children ]ような自分のヘルパーがいますが、他の人の視点を知りたいと思っていました。

これは、React要素をリストから計算式に変更した場合、型の制限を維持しながら実際に可能になるはずです。

div {
    className "myClass"

    div { ... }
}

CEの実装により、コードベースのサイズが大きくなり、保守が確実に難しくなります。また、propsが定義されているときにプリミティブなどを処理するのは少し注意が必要です。現在のように開発中ではなく、コンパイルに失敗する必要があります。

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

私がよくわからないのは、FableがCE配管の一部またはすべてを適切にコンパイルする方法と、操作を検出可能にする方法です(現在、 propタイプの場合のように)。

ディスカッショントピックを示すためにタイトルを変更する請願? 😆

@Dzoukrこれについては以前に議論がありました-私は間違いなくあなたの味方です。

ヘルパー関数は、厄介なダブルリスト構文を復活させ、基本的にこのlibの主な利点の1つを最初から排除し、div + class以上の実際のpropの使用には役立たないため理想的ではありません。

@ Zaid-Ajajこの領域を探索するために、別の名前空間を利用できますか? Feliz.Experimentalか何かのように? したがって、メインのFeliz名前空間は、Reactよりも薄い層の目標を維持しますが、代替はより意見の分かれるアプローチを試すことができます。

とにかく、私は小道具のグループ化を維持するためにこのようなものが好きです:

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

小道具は要素のように無限に入れ子になることがないため、小道具の入れ子は要素の入れ子よりもきれいに見えます。 propsコンテナは、 Html.*要素とインターフェイスを共有し、両方を含む要素の有効な子としてモデル化します。

パフォーマンスは注目に値するものですが、試してみないとその影響はわからないと思います。 誰が知っているか、多分余分な仕事は恐ろしいように聞こえるだけですが、レンダリングで起こっている他のすべてに比べて実際には取るに足らないことがわかります。

@zanaptakあなたが言うように、小道具は子供たちのように深く巣を作る傾向がないので、私はそのアイデアがとても好きです。


ヘルパーとダブルリスト構文のトピックについて

彼らは単なる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"
]

必要に応じて、任意の要素で「ダブルリスト」のような構文を使用する演算子を定義することもできます。

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

「見栄えが良い」ものは主観的である可能性があるため、これはトリッキーなトピックです。 おそらく最も重要なことは、変更を壊さないようにすることですが、実験したいだけの場合は、Reactに非常に簡単に適応できるため、 Feliz.Engineを使用できます。 他の問題で述べたように、Feliz.Engine.Reactを公開したくないのは、2つの類似した「競合する」パッケージがあると混乱するからです。 しかし、たとえば、すべてを同じレベルにしたい場合は、次のようなモジュールが必要です(別のリストに小道具を含めるように調整するのも簡単です)。

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

テスト:

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

let myElAsReactEl = myEl.AsReactEl // ReactElement 

ヘルパー関数は、厄介なダブルリスト構文を取り戻すため、理想的ではありません。

@zanaptakヘルパー関数は入力として2つのリストを取得する必要はありません。 div [ "class" ] [ ]は、たまたま2つのリストを取得する一般的なシナリオの例にすぎません。 これらの関数は、ビューを単純化するコンポーネントにすることもできます

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

技術的にはリストを無限にネストすることができますが、それは考えではありません。 大きなUIコードで小さな関数やコンポーネントを使用することで、バランスが取れています。

ただし、理想的には、コードを頻繁に分解する必要はなく、整理することを考えると_flow_が失われることがあるため、1つの関数をできるだけ読みやすくする必要があります。 これは、Fable.ReactのDSLの問題であり、毎回フォーマットする方法について_考える_必要がありました。 Felizは、 childrenつのレベルのネストを犠牲にしてそれを修正します。

正直なところ、銀の弾丸が何であるかはわかりません。 @alfonsogarciacaroが言ったように

「見栄えが良い」ものは主観的である可能性があるため、これはトリッキーなトピックです

実験のトピックについて:

この領域を探索するために、別の名前空間を利用できますか? Feliz.Experimentalか何かのように? したがって、メインのFeliz名前空間は、Reactよりも薄い層の目標を維持しますが、代替はより意見の分かれるアプローチを試すことができます。

現時点では、Felizプロジェクトで異なるDSLを維持する計画はありません。 私はOSSの時間/容量に非常に制限があり、現在のアプローチ(ドキュメント、サンプル、およびより十分にテストされたエコシステムライブラリ)の改善に努力を向けることを望んでいます。

そうは言っても、@ alfonsogarciacaroによるFeliz.Engineを自由に試して、自分に

このページは役に立ちましたか?
0 / 5 - 0 評価