Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ReactComponentAttribute for props list #581

Open
lukaszkrzywizna opened this issue Sep 4, 2023 · 2 comments
Open

ReactComponentAttribute for props list #581

lukaszkrzywizna opened this issue Sep 4, 2023 · 2 comments

Comments

@lukaszkrzywizna
Copy link
Contributor

lukaszkrzywizna commented Sep 4, 2023

Hi,
First of all, I love Feliz! I very much like the paradigm it offers, especially the IReactProperty list approach. It's very readable and closest to the original JS/JSX approach.

However, when I use it in conjunction with ReactComponent attribute I see one drawback - created react component instead of containing an object with props taken from the list, it has one single prop of a list type. In most cases it doesn't matter, but based on react-memo article text:

When a component visually wraps other components, let it accept JSX as children. This way, when the wrapper component updates its own state, React knows that its children don’t need to re-render.

we can assume that react somehow by checking what kind of props are passed.

Additionally, using traditional object param is more convenient when using React developer tools:
For code

[<ReactComponent>]
let Tst1 (props: IReactProperty seq) =
    Html.h1 [
        prop.style [ style.color.red ]
        yield! props
    ]

[<ReactComponent>]
let Tst2 (text: string, ariaLabel: string) =
    Html.h1 [
        prop.style [ style.color.red ]
        prop.text text
        prop.ariaLabel ariaLabel
    ]
image versus image

The easiest solution is to manually translate prop-list into an object. However, I wonder if we could do something in the scope of Feliz.CompilerPlugin.

I imagine that by using a special attribute (ReactComponent or something new), the plugin could detect if a function expects a single prop of a list type and map the input into an object by calling Object.fromEntries and within the function unwrap it back by using Object.entries. #580 is a small POC that illustrates the solution:

[<ReactListComponent>]
let Tst2 (props: IReactProperty seq) =
    Html.h1 [
        prop.style [ style.color.red ]
        yield! props
    ]

Tst2 [ prop.text "My text" ]

gives something similar to this

export function Tst2(tst2InputProps) {
    const props = (($value) => Object.entries($value))(tst2InputProps);
    return createElement("h1", createObj(toList(delay(() => append(singleton(["style", {
        color: "#FF0000",
    }]), delay(() => props))))));
}

createElement(Tst2, (($value) => Object.fromEntries($value))([["children", "Some text"]]))

We can go further, and extend the existing tuple-component approach by allowing to put an extra list parameter that would be merged with the rest of the explicitly provided params (not implemented):

[<ReactListComponent>]
let Tst2 (className: string, props: IReactProperty seq) =
    Html.h1 [
        prop.style [ style.color.red ]
        prop.className className
        yield! props
    ]
Tst2 ("my-class", [ prop.text "Some text"])
export function Tst2({ className, ...tst2InputProps }) {
    const props = (($value) => Object.entries($value))(tst2InputProps);
    return createElement("h1", createObj(toList(delay(() => append(singleton(["style", {
        color: "#FF0000",
    }]), delay(() => append(singleton(["className", className]), delay(() => props))))))));
}

createElement(Tst2, {
        className: "my-class",
        ...((($value) => Object.fromEntries($value))([["children", "Some text"]])),
    })

@Zaid-Ajaj I'd like to know your opinion about that. If you think that this makes sense, I can continue developing the solution. I'm open to any other option/approach.

@Zaid-Ajaj
Copy link
Owner

Hi there @lukaszkrzywizna sorry for the delayed response 🙏 been busy with day job, hard to keep track of everything going on in the OSS space.

About IReactProperty list I really think of this as purely an interop mechanism when binding third-party components. If you are building your own components in pure F#, use normal parameters with [<ReactComponent>].

Using [<ReactListProperty>] does sound interesting for niche use cases, in general I believe [<ReactComponent>] should do the trick but i agree that ReactListProperty is more flexible. To be honest I am a bit reluctant on adding more ways to implement these React components but I will think about it more

@lukaszkrzywizna
Copy link
Contributor Author

No problem @Zaid-Ajaj I know you're busy man :) I'll try to investigate it further in a free time and share something more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants