Feliz: Ваше мнение об идее v2?

Созданный на 17 мар. 2021  ·  9Комментарии  ·  Источник: Zaid-Ajaj/Feliz

Привет друг,
как ежедневный пользователь Feliz (и на самом деле Feliz.Bulma) я всегда борюсь с вложением prop.children что делает его как-то уродливее для меня. Я хотел бы узнать ваше мнение о предложении v2, где мы бы:

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

Может быть , что - то подобное даже не представляется возможным, но я видел @alfonsogarciacaro «s работы на Feliz.Engine и, кажется , так заманчиво иметь реквизит и ребенок на тот же уровень. 😄

discussion

Самый полезный комментарий

Петиция об изменении названия с указанием темы обсуждения? 😆

@Dzoukr Это уже обсуждалось ранее - я определенно на вашей стороне.

Вспомогательные функции не идеальны, потому что они возвращают неудобный синтаксис двойного списка, в первую очередь устраняя одно из ключевых преимуществ этой библиотеки, и они не помогают с реальным использованием пропов, что намного больше, чем просто класс div +.

@ Zaid-Ajaj Не могли бы вы использовать альтернативное пространство имен, чтобы исследовать эту область? Например, Фелиз.Экспериментал или что-то в этом роде? Таким образом, основное пространство имен 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"
]

Мне кажется, нет причин не писать свои собственные методы расширения, если они лучше подходят для эффекта применения классов или других часто используемых свойств, как я сделал здесь с Material UI. С 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 увеличит размер кодовой базы, определенно усложнит ее поддержку, а обработка некоторых вещей, таких как примитивы, когда определены реквизиты, немного сложна / могла бы потерпеть неудачу при компиляции, а не во время разработки, как сейчас:

// 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 Это уже обсуждалось ранее - я определенно на вашей стороне.

Вспомогательные функции не идеальны, потому что они возвращают неудобный синтаксис двойного списка, в первую очередь устраняя одно из ключевых преимуществ этой библиотеки, и они не помогают с реальным использованием пропов, что намного больше, чем просто класс div +.

@ Zaid-Ajaj Не могли бы вы использовать альтернативное пространство имен, чтобы исследовать эту область? Например, Фелиз.Экспериментал или что-то в этом роде? Таким образом, основное пространство имен 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" ]

Это непростая тема, потому что то, что «выглядит лучше», может быть субъективным. Вероятно, самое важное - избегать критических изменений, но если вы просто хотите поэкспериментировать, вы можете использовать для этого Feliz.Engine , так как его очень легко адаптировать к React. Как уже упоминалось в других вопросах, я не хочу публиковать Feliz.Engine.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))

Контрольная работа:

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

let myElAsReactEl = myEl.AsReactEl // ReactElement 

Вспомогательные функции не идеальны, потому что они возвращают неудобный синтаксис двойного списка.

@zanaptak Вспомогательные функции не должны принимать в качестве входных данных два списка, div [ "class" ] [ ] был просто примером распространенного сценария, в котором использовалось два списка. Эти функции также могут быть компонентами, упрощающими просмотр.

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

Хотя технически вы можете вкладывать списки бесконечно, идея не в этом. Здесь достигается баланс за счет использования более мелких функций или компонентов в более крупном коде пользовательского интерфейса.

Однако в идеале вам не нужно так часто ломать код, и одна функция должна быть настолько удобочитаемой, насколько это возможно, потому что размышления об организации вещей иногда забирают _flow_. Это была проблема с DSL в Fable.React, когда вам приходилось каждый раз _ думать_ о том, как форматировать вещи. Фелиз исправляет это за счет двух уровней вложенности children .

Если честно, я не знаю, что такое серебряная пуля. Как сказал @alfonsogarciacaro

Это непростая тема, потому что то, что «выглядит лучше», может быть субъективным.

По теме экспериментов:

Вы бы открыли альтернативное пространство имен для изучения этой области? Например, Фелиз.Экспериментал или что-то в этом роде? Таким образом, основное пространство имен Feliz сохраняет цель тонкого слоя над React, в то время как альтернативное может попробовать более самоуверенный подход.

На данный момент нет планов по поддержке различных DSL в проекте Feliz. Я очень ограничен во времени / возможностях OSS и надеюсь направить усилия на улучшение текущего подхода: документация, образцы и более хорошо протестированные экосистемные библиотеки.

Тем не менее, не стесняйтесь экспериментировать с Feliz.Engine от @alfonsogarciacaro и использовать DSL, который вам больше подходит.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги