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

Problem setting component' property to a ReactElementType #67

Open
inouiw opened this issue Jan 22, 2021 · 9 comments
Open

Problem setting component' property to a ReactElementType #67

inouiw opened this issue Jan 22, 2021 · 9 comments

Comments

@inouiw
Copy link

inouiw commented Jan 22, 2021

The component' property accepts a string or a ReactElementType.

I want to set the component' property of tableContainer to Mui.paper. The code below compiles but then I the following error in the console

Seq.js:34 Uncaught TypeError: o[Symbol.iterator] is not a function
    at getEnumerator (Seq.js:34)
    at fromFlatEntries (Flatten.fs.js:33)
    at MuiHelpers_createElement (Mui.fs.js:6)
    at Helptext.fs.js:13
    at renderWithHooks (react-dom.development.js:14985)
    at mountIndeterminateComponent (react-dom.development.js:17811)
    at beginWork (react-dom.development.js:19049)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
    at invokeGuardedCallback (react-dom.development.js:4056)

The generated JavaScript is:

export const helptextElement2 = MuiHelpers_createElement(TableContainer, [["component", arg00 => MuiHelpers_createElement(Paper, arg00)], ["children", reactApi.Children.toArray([MuiHelpers_createElement(Table, [["size", "small"], ["children", reactApi.Children.toArray([MuiHelpers_createElement(TableHead, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableRow, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableCell, [["children", "col 1"]]), MuiHelpers_createElement(TableCell, [["children", "col 2"]])])]])])]]), MuiHelpers_createElement(TableBody, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableRow, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableCell, [["children", "lala"]]), MuiHelpers_createElement(TableCell, [["children", "liauh"]])])]])])]])])]])])]]);

The F# code is:

open Feliz
open Feliz.MaterialUI

// https://material-ui.com/components/tables/#basic-table
let helptextElement2 =
  Mui.tableContainer [
    tableContainer.component' (Fable.React.ReactElementType.ofFunction Mui.paper)
    tableContainer.children [
      Mui.table [
        table.size.small
        table.children [
          Mui.tableHead [
            Mui.tableRow [
              Mui.tableCell "col 1"
              Mui.tableCell "col 2"
            ]
          ]
          Mui.tableBody [
            Mui.tableRow [
              Mui.tableCell "lala"
              Mui.tableCell "liauh"
            ]
          ]
        ]
      ]
    ]
  ]

It would be nice if somebody could help me with the problem.

@cmeeren
Copy link
Collaborator

cmeeren commented Jan 22, 2021

I'm always confused where ReactElementType is involved. @Zaid-Ajaj, do you know how this works?

@inouiw
Copy link
Author

inouiw commented Jan 23, 2021

Hi,

I got a working solution. It requires creating a function. Since I did not know what type the argument should be, I inserted a console.log in the function to see what was passed and then created the IPaperProps type.
A solution without needing to create a function would be nice although it is not urgent for me.

Edit: removed [<ReactComponent>] because just a function is needed.

open Feliz
open HtmlEx
open Feliz.MaterialUI

type IPaperProps =
  abstract member className: string
  abstract member children: ReactElement []

let PaperComponent (props: IPaperProps) =
  Mui.paper props.children

// https://material-ui.com/components/tables/#basic-table
let helptextElement2 =
  Mui.tableContainer [
    tableContainer.component' (Fable.React.ReactElementType.ofFunction PaperComponent)
    tableContainer.children [
      Mui.table [
        table.size.small
        table.children [
          Mui.tableHead [
            Mui.tableRow [
              Mui.tableCell "col 1"
              Mui.tableCell "col 2"
            ]
          ]
          Mui.tableBody [
            Mui.tableRow [
              Mui.tableCell "lala"
              Mui.tableCell "liauh"
            ]
          ]
        ]
      ]
    ]
  ]

@cmeeren
Copy link
Collaborator

cmeeren commented Jan 23, 2021

Yes, that seems like an ugly and brittle workaround. I think there are better ways, but I'm not sure how. Perhaps @Zaid-Ajaj or other Fable/React wizards know this? 🧙‍♂️

@Shmew
Copy link
Owner

Shmew commented Jan 23, 2021

So I believe this would work: tableContainer.component' (importDefault "@material-ui/core/Paper").

The library could be adjusted to have something like this:

type MuiTypes =
	static member inline paper : ReactElementType = importDefault "@material-ui/core/Paper"

@inouiw
Copy link
Author

inouiw commented Jan 23, 2021

@Shmew thanks. That works, and using MuiTypes it looks nice.
Maybe also consider adding a method MuiTypes.fromReactElement (elem: ReactElement) and/or ReactElementType.fromReactElement (elem: ReactElement)

@Shmew
Copy link
Owner

Shmew commented Jan 24, 2021

Does that work for you for any component you define? I haven't personally tried it, I don't think that will work with any imported components (which is why we need the MuiTypes here).

@inouiw
Copy link
Author

inouiw commented Jan 24, 2021

@Shmew sorry I am not sure if I understand your question.

By the way the version with MuiTypes compiles to

MuiHelpers_createElement(TableContainer, [["component", Paper], ["children", reactApi.Children.toArray([MuiHelpers_createElement(Table, [["size", "small"], ["children", reactApi.Children.toArray([MuiHelpers_createElement(TableHead, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableRow, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableCell, [["children", "Command"]]), MuiHelpers_createElement(TableCell, [["children", "Keybinding"]]), MuiHelpers_createElement(TableCell, [["children", "When"]])])]])])]]), MuiHelpers_createElement(TableBody, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableRow, [["children", reactApi.Children.toArray([MuiHelpers_createElement(TableCell, [["children", reactApi.Children.toArray([(() => {

Paper is declared in Paper.d.ts as export default function Paper(props: PaperProps): JSX.Element;

I like the MuiTypes approach. Though I am thinking if it could be more easily discoverable.
The most intuitive would be if I could just write

tableContainer.component' Mui.Paper

Instead of

tableContainer.component' MuiTypes.Paper

But that would mean that for every "component'" method there needs to be an additional overload that does some conversion.

The following alternatives may also be worth to consider:
tableContainer.component' ReactElementType.Paper
tableContainer.component' MuiTypes.Paper.ElementType

@Shmew
Copy link
Owner

Shmew commented Jan 24, 2021

Yeah the issue is Mui.paper won't work because it's expecting a list of properties, not the actual object. I was asking if you had a component like React.functionComponent(fun props -> Html.div []) does it work for tableContainer.component'.

@cmeeren
Copy link
Collaborator

cmeeren commented Feb 5, 2021

Adding a MuiTypes module as described by @Shmew in #67 (comment) seems simple, but I'm still fuzzy enough about ReactElementType and associated APIs that I'm unsure whether this is the best solution. I don't have better options, I'd just like someone knowledgeable about these matters to reassure me (or provide a better alternative).

Will probably do this at some point if no-one picks up this torch, but it'll be done sooner if someone can help me out with these clarifications.

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

3 participants