Fable icon indicating copy to clipboard operation
Fable copied to clipboard

[JSX] Improve JSX "detection" for function call

Open Freymaurer opened this issue 8 months ago • 5 comments

Issue

Currently if a function is not decorated with the [<JSX.ComponentAttribute>], it will be called as function and not JSX.Component. For Feliz this is a rather breaking change as you only had to decorate functions using React with the [<ReactComponent>] attribute. It would be nice to improve detection of JSX.Element to automatically refer to it with Component Syntax.

The following code showcases this

// [<JSX.ComponentAttribute>]
let Component1 () =
  JSX.create "div" [
    "id", "InnerId"
    "children", "Hello World!"
  ]

let Component2() =
  JSX.create "div" [
    "children", [
      box "Title"
      box 12
      Component1() |> box
    ]
  ]

Output:

// with [<JSX.ComponentAttribute>]
export function Component1() {
    return <div id="InnerId">
        Hello World!
    </div>;
}

export function Component2() {
    return <div>
        Title
        {12}
        <Component1></Component1>
    </div>;
}
// without [<JSX.ComponentAttribute>]
export function Component1() {
    return <div id="InnerId">
        Hello World!
    </div>;
}

export function Component2() {
    return <div>
        Title
        {12}
        {Component1()}
    </div>;
}

Alternative Example

The example above can be avoided by using the attribute, but here is an example with a subcomponent defined inside the main component for better readability:

[<JSX.ComponentAttribute>]
let Component2() =

  let subcomponent() =
      JSX.create "div" [
        "id", "InnerId"
        "children", "Hello World!"
      ]

  JSX.create "div" [
    "children", [
      box "Title"
      box 12
      subcomponent() |> box
    ]
  ]

And output:

export function Component2() {
    const subcomponent = () => <div id="InnerId">
        Hello World!
    </div>;
    return <div>
        Title
        {12}
        {subcomponent()}
    </div>;
}

Freymaurer avatar May 08 '25 11:05 Freymaurer

This isn't an issue in Solid-js, as components are just function calls.

For me personally, I think this is correct. Better that component rendering is explicit rather than implicit and potentially breaking.

shayanhabibi avatar May 09 '25 13:05 shayanhabibi

For Feliz this is a rather breaking change as you only had to decorate functions using React with the [<ReactComponent>] attribute

To be more precise, we needed [<ReactComponent>] when we wanted to use React API to access hooks and stuff like that. Or follow React philosophy of using props for passing information instead of arguments.

It is also possible that by generating {subcomponent()} React tooling will not report the component in the dev tool DOM.

I remember that in some cases generating code like {subcomponent()} was difficult with Feliz so perhaps we should be careful about not removing a feature we could need in the future.

I am not 100% certain yet of what should be decided here.

MangelMaxime avatar May 09 '25 14:05 MangelMaxime

An operator or function could be introduced which forces a function call that outputs a JSX.Element to compile as a tag. You could then create a helper that combines that with box (since there is already element decoration in the example).

This seems like a Plugin thing though

shayanhabibi avatar May 09 '25 14:05 shayanhabibi

An operator or function could be introduced which forces a function call that outputs a JSX.Element to compile as a tag.

[<JSX.ComponentAttribute>] seems to be doing that even if it rewrites the function from:

function MyComponent(arg1, arg2)

to

function MyComponent(props) {
	// Usage
	// props.arg1
	// props.arg2
}

I don't think it matters much if the argument are rewritten to a props objects.

MangelMaxime avatar May 09 '25 14:05 MangelMaxime

Indeed, but I think Freymauer wants to avoid explicit decoration of a function that should be compiled as a tag.

To clarify though, I am 100% for explicit decoration over implicit conversion. This is why I think, if people wanted a more concise/implicit conversion, it would be better suited to a plugin to resolve, rather than introduced as implicit behaviour for JSX.

ie: my opinion is this is not an 'issue', and is instead correct output.

shayanhabibi avatar May 09 '25 15:05 shayanhabibi