GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Add an example which includes an external component · Issue #2 · purescript-react/purescript-react-basic

Add an example which includes an external component #2

New issue
Copy link
New issue
Copy link
Open
Open
Add an example which includes an external component#2
Copy link

Description

@paf31
paf31
opened on Jan 6, 2018
Issue body actions

It would be good to demonstrate that we can use other components easily.

Activity

  1. maddie927 commented on Jan 10, 2018

    @maddie927
    maddie927
    on Jan 10, 2018
    Member
    More actions

    Would this include to/from functions for purescript-react's ReactComponent type? I'm curious if you already have something in mind for the non-basic cases. I've been thinking about something similar to this but as convenience functions for common patterns in or on top of purescript-react so it's easy to reach for a lifecycle function when you need it. I really like the props setup going on in this library though.

  2. maddie927 commented on Jan 10, 2018

    @maddie927
    maddie927
    on Jan 10, 2018
    Member
    More actions

    Specifically, I was thinking about stealing reason-react's "reducer" component (very similar to elm/pux/redux, but using component state).

    Examples:
    createStatefulComponent (\props -> initialState) reducer render
    createStatefulComponentWithInit InitAction (\props -> initialState) reducer render (componentDidMount)
    createFormContainer ??? (don't know how this would work yet but I want it 🙂)

  3. paf31 commented on Jan 10, 2018

    @paf31
    paf31
    on Jan 10, 2018
    ContributorAuthor
    More actions

    I was thinking more about components from NPM. I haven't really thought too much about adapters, but I think I would prefer for them to be in a separate library. I considered going via purescript-react, but I wanted as few dependencies as possible and the freedom to try different APIs out easily.

    This library is pretty much tailor-fit for our use cases right now, but if the need for lifecycle methods comes up, we'll have to find a way to include them, hopefully without complicating the API.

    Yes, I expect a Redux/ElmArch layer on top of this would be quite useful.

    Reacted by maddie, Brady Ouren and Steven MacCoun
  4. paf31
    closed this as completedon Feb 21, 2018
  5. jgoux commented on Apr 14, 2018

    @jgoux
    jgoux
    on Apr 14, 2018
    More actions

    Hello,

    were these examples ever added?

    I'd be interested in wrapping external components 🌯

  6. paf31 commented on Apr 16, 2018

    @paf31
    paf31
    on Apr 16, 2018
    ContributorAuthor
    More actions

    Not yet, although they basically amount to using the FFI to pull in the component class as a value and then using it as normal.

  7. maddie927 commented on Apr 16, 2018

    @maddie927
    maddie927
    on Apr 16, 2018
    Member
    More actions

    Example for (simplified) React Router Route component:

    type RouteProps =
      { path :: String
      , exact :: Boolean
      , render :: MatchedRouteProps -> JSX
      }
    
    foreign import route :: ReactComponent RouteProps
    Reacted by CT Wu and Julien Goux
  8. i-am-the-slime commented on May 17, 2019

    @i-am-the-slime
    i-am-the-slime
    on May 17, 2019
    Member
    More actions

    But what would the js file look like?

  9. maddie927 commented on May 17, 2019

    @maddie927
    maddie927
    on May 17, 2019
    Member
    More actions

    Something like this:

    "use strict";
    
    var RR = require("react-router");
    
    exports.route = RR.Route;
  10. i-am-the-slime commented on May 18, 2019

    @i-am-the-slime
    i-am-the-slime
    on May 18, 2019
    Member
    More actions

    Thank you very much, for my example (a material UI button) it was this:

    foreign import button ::
      ReactComponent { onClick :: EventHandler, children :: Array JSX, color :: String, variant :: String }
    'use strict'
    exports.button = require("@material-ui/core/Button").default;

    I'm also curious how I could make all the fields of the props somehow optional.

  11. maddie927 commented on May 20, 2019

    @maddie927
    maddie927
    on May 20, 2019
    Member
    More actions

    You can use the same Union trick we used here: https://github.com/lumihq/purescript-react-basic/blob/master/src/React/Basic/DOM/Generated.purs#L119

    This only works for FFI -- the type it creates can't really be implemented using PureScript. You can make some fields required by putting them in the Record type instead of the Union:

    a
      :: forall attrs attrs_
       . Union attrs attrs_ Props_a
      => { someRequiredField :: Asdf | attrs }
      -> JSX
  12. alextes commented on May 24, 2020

    @alextes
    alextes
    on May 24, 2020
    More actions

    Can this be reopened with the outcome of having an example? Or even multiple examples? There appears to be no information yet on how to deal with external components. Or perhaps as it appears there is no one true way, what I'm looking for is an overview of the options people have thought of. I'll share mine.

    The following is all in the context of larger external components as often found in frameworks or libraries.

    1. ReactComponent with lots of Data.Nullable. This feels cumbersome to me at around five props up, where three or more props are passed null. It makes the types accurate and helpful, but it makes the JSX communicate a lot of nothing. Additionaly, as you need more props exposed from the external component you have to go back and update all the uses with nulls, or spend time up front to type props you don't yet care about.

    2. forall a. ReactComponent (Record a) obviously this is not the PureScript way but nevertheless I notice myself reaching for it as an escape hatch for components with many optional props that are all over the place. For example a button component. Yet, my frustration with the unsafe type brought me here 😄 .
      2a. I just thought of this one, I guess you could type the component as if it wants the full record, pass a record that is a subset of it and unsafeCoerce it. At least that way you get to keep the types as information.

    type ButtonProps = { onClick :: Effect Unit, disabled :: Boolean }
    foreign import button ::
    	forall props props_. Union props props_ ButtonProps =>
    	ReactComponent (Record props)

    It appears easy to break. I broke it, by moving the union outside the foreign import. Spent time understanding FFI more deeply, fixing it. Similarly, this method appears to break down when some props are sets of props themselves.

    1. Use Data.Options. Designed for this scenario. I only tried it superficially myself. Creating the sets of props is a breeze, on the other hand, extra syntax in the middle of JSX, high boilerplate, what about props that are shared between many components? Like children?
    Reacted by Mark Eibes
  13. jvliwanag commented on May 24, 2020

    @jvliwanag
    jvliwanag
    on May 24, 2020
    Contributor
    More actions

    I need to clean up my lib -- https://github.com/jvliwanag/purescript-oneof . But adding it as an option as well.

    It allows for simple record declarations:

    type Props = 
      { name :: UndefinedOr String
      , foo :: UndefinedOr (String |+| Number |+| Boolean)
      }
    
    foreign import _myButton :: ReactComponent Props
    
    myButton :: forall r. Coercible r Props => r -> JSX
    myButton = element _myButton <<< coerce
    
    sample = myButton { foo: "bar" } -- can omit name
    Reacted by Alexander Tesfamichael and Adam Barber
  14. maddie927 commented on May 26, 2020

    @maddie927
    maddie927
    on May 26, 2020
    Member
    More actions

    I wouldn't mind having an FFI example. You're correct that it's more because there's no one way to do it, and it's not really different from any other PureScript FFI.

    1. When I'm making a PS component which wraps a JS one and that JS component is otherwise hidden from the outside world, I reduce the Nullable noise by not defining props I won't use and making props I always use non-nullable, even if they're technically optional in the JS component. For example, if I'm wrapping a button component I will probably define onClick :: EventHandler and just always provide it, even if there are cases where it's a noop.
    • btw, you can define a "defaults" props record alongside your component, allowing you to call it like Button.button Button.defaults { onClick = do something } -- this allows you to add new props to the component without breaking existing usage, and this isn't specific to FFI components
    1. Yeah, once in a while I do this to hack something quickly, but options 1 or 3 are almost always better.

    2. This is great for simple components with lots of props, like the DOM components. It's useful when the FFI component is extremely flexible and you need to expose that in PS without lots of boilerplate. Like you said, though, it can have issues if any props have complicated types, like nested records. I would generally recommend the Data.Options approach.

    3. Yep, this is a good option

    4. One other option which is sometimes appropriate -- split the definition into two or more components. Sometimes certain props are required in combination. Defining the component multiple times with different props can simplify basic use cases while also making more complicated ones safer.

    Reacted by Mark Eibes
  15. alextes commented on May 27, 2020

    @alextes
    alextes
    on May 27, 2020
    More actions

    Lots of good options, some new to me. Thanks!

  16. maddie927
    reopened this on May 27, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.