fable-compiler / fable-react

Fable bindings and helpers for React and React Native
MIT License
275 stars 66 forks source link

What's the replacement for ReactNode, ReactType, and ReactInstance? #151

Open cmeeren opened 5 years ago

cmeeren commented 5 years ago

I'm currently making a PR to update Fable.MaterialUI to Fable.Core 3 and Fable.React 5. The API makes use of ReactNode, ReactType, and ReactInstance, none of which are available any longer.

Should ReactElement be used instead for all of these?

If you need more context to answer this question, here are examples in the (original) MaterialUI documentation where the three types are used in the current bindings:

alfonsogarciacaro commented 5 years ago

In Fable.React 5 we've tried too simplify the interfaces as they were translated from the React Typescript definitions because they were quite complicated. Looking at the samples you kindly provided the equivalences seem to be:

type MenuProps =
   | AnchorEl of ReactElement
   | [<CompiledName("anchorEl")>] AnchorElType of ReactElementType
cmeeren commented 5 years ago

Thanks for the quick reply!

What is actually ReactElement and ReactElementType? In this context, what is the usage difference between them in F# for such a props API? What is possible with one that the other does not allow for?

Also, ReactElementType is generic, but I have no idea what props type to supply because none of the three previous types are generic. Do you have any idea? Does it make sense to supply obj?

alfonsogarciacaro commented 5 years ago

ReactElement are instances of React elements, that is, nodes in the Virtual DOM. ReactElementType are constructors for such elements (basically the components, although the word "component" is usually associated with classes). You can build an element using ReactElementType.create, but most of the times you will use other helpers.

Again, this is a simplification and you may find different wording/concepts when checking React documentation or tutorials.

It's true that the generic makes it difficult to create a prop accepting any kind of ReactElementType, so I just added a non generic version in Fable.React 5.1.0. Now you can do:

// In fable-material-ui
    type MaterialProp =
        | Component of ReactElementType
       ..

// Usage

let view (props: {| title: string |}) =
    h1 [] [str props.title]

let menuProps =
    [ Component (ReactElementType.ofFunction view) ]
cmeeren commented 5 years ago

Thanks! Great to have the non-generic one.

Given a component from a 3rd party library, how would I get a ReactElementType of that?

MangelMaxime commented 5 years ago

@cmeeren You could just use something like:

let inline bigCalendar (props : BigCalendarProps list) : ReactElement = 
    ofImport "default" "react-big-calendar" (JsInterop.keyValueList CaseRules.LowerFirst props) []

This is what I use in all my bindings and it works. I am not sure why you would need to call ReactElementType interface yourself. This interface is a really low level thing and most of the timùe you should be able to not use it I think.

cmeeren commented 5 years ago

@MangelMaxime For an example, check the MUI Input API, specifically the inputComponent prop. An example usage is given in Text Fields -> Formatted Inputs. Expand the code for that section and search for inputComponent. You can see that it isn't passed an instance (which AFAIK would be ReactElement), but rather the component itself (dunno if that was the correct term). The most important code is below:

function TextMaskCustom(props) {
  ...  // component implementation
}

...

class FormattedInputs extends React.Component {
  ...
  render() {
    ...
    return (
      ...
          <Input
            ...
            inputComponent={TextMaskCustom}
          />
        ...
  }
}

TextMaskCustom is a functional component, but I would guess (I might be wrong) that inputComponent works the same way with class components too.

Are you saying I still don't need ReactElementType for the inputComponent prop?

MangelMaxime commented 5 years ago

@cmeeren Ah this is the first time I this kind of code. Then I don't know sorry 😅

React is really cool but sometimes it's really too permissive. I mean there are so many ways and hacks possible to write a component. ^^

alfonsogarciacaro commented 5 years ago

Just import it as ReactElementType (with props typed or not depending on your needs):

// Check the documentation of the component to know if you should import the default or a named member
// See https://fable.io/docs/interacting.html#importing-javascript-code

let BigCalendar: ReactElementType<BigCalendarProps> = importDefault "react-big-calendar"