Skip to content

Alternative external component representation #139

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

Closed
ethul opened this issue Apr 1, 2018 · 3 comments
Closed

Alternative external component representation #139

ethul opened this issue Apr 1, 2018 · 3 comments

Comments

@ethul
Copy link
Contributor

ethul commented Apr 1, 2018

In addition to the representation described in #96 for foreign components, sometimes it is handy to represent an externally defined component as follows:

bar :: Array Props.Props -> Array React.ReactElement -> React.ReactElement
bar props children = React.createElement barComponent (Props.unsafeFromPropsArray props) children                                                          

someBarProp :: String -> Props.Props
someBarProp = Props.unsafeMkProps "someBarProp"

foreign import barComponent:: forall r. React.ReactClass { | r }

In this representation one is able to pass any of the props defined in React.DOM.Props. This can be useful if the external component forwards props to an underlying DOM element, for example.

However, this representation fails to compile with:

[1/1 NoInstanceFound] src/...

  75  bar props children = React.createElement barComponent (Props.unsafeFromPropsArray props) children
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

  No type class instance was found for

    Prim.Union t1
               t2
               ( key :: String
               , ref :: EventHandler (Nullable Ref)
               | t0
               )

  The instance head contains unknown type variables. Consider adding a type annotation.

  while applying a function createElement
    of type ReactPropFields t0 t1 => ReactClass
                                       { children :: Children
                                       | t0
                                       }
                                     -> { | t1 } -> Array ReactElement -> ReactElement
    to argument barComponent
  while inferring the type of createElement barComponent
  in value declaration bar

  where t1 is an unknown type
        t0 is an unknown type
        t2 is an unknown type

I am wondering if there is a way to implement this kind of representation or something similar.

//cc @natefaubion

@natefaubion
Copy link
Contributor

This is fundamentally unsafe, so I'm not sure that a ReactClass is the best representation. I could see providing an unsafeCreateElement function without constraints, and for this case you would export it partially applied, rather than the ReactClass just as the DOM constructors are done.

module Foo (foo) where

import React (ReactClass, ReactElement, unsafeCreateElement)
import React.DOM.Props (Props, unsafePropsFromArray)

foreign import fooClass :: forall r. ReactClass { | r }

foo :: Props -> Array ReactElement -> ReactElement
foo = unsafeCreateElement fooClass <<< unsafePropsFromArray

You don't have the issue with key and ref because those are props in DOM and are implicitly allowed.

@natefaubion
Copy link
Contributor

Actually, you probably don't even need that. Since unsafePropsFromArray creates a { | r }, then you can just add a type annotation to fix r to (), and it would work.

@ethul
Copy link
Contributor Author

ethul commented Apr 2, 2018

Yes. You're right. Good call on the annotation. Thanks!

Full example:

module Bar (bar) where

bar :: Array Props.Props -> Array React.ReactElement -> React.ReactElement
bar props children = React.createElement barComponent (Props.unsafeFromPropsArray props :: Record ()) children

someBarProp :: String -> Props.Props
someBarProp = Props.unsafeMkProps "someBarProp"

foreign import barComponent:: React.ReactClass (Record (children :: React.Children))

@ethul ethul closed this as completed Apr 2, 2018
@ethul ethul reopened this Apr 2, 2018
ethul added a commit that referenced this issue Apr 14, 2018
Resolves #96
Resolves #105
Resolves #138
Resolves #139
ethul added a commit that referenced this issue Apr 14, 2018
Resolves #96
Resolves #105
Resolves #138
Resolves #139
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants