v4 API#58
Conversation
f1778ce to
1876e68
Compare
| } | ||
|
|
||
| component :: StatelessComponent | ||
| component = createStatelessComponent "Container" |
There was a problem hiding this comment.
Example of a stateless component. API is not that different. Note the pre-applied prop: render = unit # .... Non-record prop and state types are now allowed, and since this component has no props it's safe to pre-apply one to avoid work.
It's also worth noting that because render will usually have the type props -> JSX the only advantage of defining a "stateless component" is to create the named wrapper component in React's dev tools. If this isn't a concern, just export a plain props -> JSX render function without using make or makeStateless.
| Nothing -> throw "Container element not found." | ||
| Just c -> | ||
| let app = element Container.component {} | ||
| let app = Container.render |
There was a problem hiding this comment.
element is now only used for rendering ReactComponents, i.e. a component imported from JS. react-basic's Component type is not a JS-friendly component and make function applies React.createElement automatically. Before this PR is complete we'll add a "to-JS" helper for JS-friendly exports.
There was a problem hiding this comment.
I added toReactComponent. It ends up just being an unsafe coerce, but it’s still an important distinction because ReactComponent reference equality matters while props -> JSX reference equality does not (meaning no type class wrappers should be present wherever toReactComponent is used)
| UpdateAndSideEffects | ||
| self.state { on = not self.state.on } | ||
| \self -> do | ||
| log $ "nextState: " <> show self.state |
There was a problem hiding this comment.
State updates are now handled by the update function, which takes Self and an action argument. There are no restrictions on the action type used, but a component-specific sum-type is recommended, as it summarizes the capabilities of the component well.
The update function should return the desired state change, including an optional effect to perform once the state change is complete. This state change is described using StateUpdate.
This function is analogous to reason-react's reducer function.
| { onClick: Events.handler_ do | ||
| setStateThen (\s -> s { on = not s.on }) \nextState -> do | ||
| log $ "nextState: " <> show nextState | ||
| { onClick: Events.handler_ do self.send Toggle |
There was a problem hiding this comment.
Component interactivity is driven by the send function. Calls to send trigger the previously described update process. Event handlers are still Effects, so you can put complicated effects here, but when it comes time to effect a change on the component itself it needs to be distilled into a single packet of changes.
For example, a SearchableTable component might have an onChange handler on a text field. It could read the field value, initiate a request, and as that request completes call send (SearchResults results). However, a better approach would be to read the field value and immediately call send (OnSearch searchTerm), and handle that action in update using SideEffects \self -> do [network >>= (self.send >>> SearchResults)]. This simplifies the render function and describes more of the component behavior in a way that's isolated from the DOM.
| }; | ||
| var defaultRender = function() { | ||
| return false; | ||
| }; |
There was a problem hiding this comment.
These default implementations keep the PureScript types honest for ComponentSpec fields which are not overridden.
|
|
||
| type Component = forall props state action. ComponentSpec props state action | ||
|
|
||
| type StatelessComponent = forall props. ComponentSpec props Void Void |
There was a problem hiding this comment.
These are here for convenience when writing components, but otherwise don't do much
|
|
||
| -- | Apply a React key to a sub-tree. | ||
| keyed :: String -> JSX -> JSX | ||
| keyed = runFn2 keyed_ |
There was a problem hiding this comment.
element and elementKeyed are now specific to JavaScript React components. keyed nests any JSX in a keyed Fragment, allowing a parent to specify keys without altering the simplified react-basic types.
68e97f0 to
10c3116
Compare
|
@justinwoo @paluh @zudov Not sure which of you are actively using this library, but we're curious what your thoughts are on these changes if you are using it. (I haven't written a readme update yet, but there is a |
|
I'm starting to use more of my own layer on top that just works via a version of the regular React component API, so I won't have much to say about this immediately. Hopefully I will get around to reading through this sometime. |
|
Ah, @f-f too, thanks! |
|
Neat! I'd love to see how it goes (and hopefully if I push a couple more tweaks it won't be too confusing -- let me know if you have questions). Also, while you can port v2/3 to this mechanically, it feels a lot better if you give the actions are thought out a bit (send/capture work better, update is more descriptive, etc). |
|
There is currently a compiler issue/limitation which shows up occasionally when using |
|
Refactored |
|
Now that React disclosed hooks (https://reactjs.org/docs/hooks-reference.html), what do you think about them and how could they be integrated with PureScript? |
|
They're somewhat monadic which is interesting. The implementation and usage in JS is concerning, but that's already the case for a lot of JS anyway. Maybe it could be exposed via PureScript in a safer way, but whether it's a nicer API would take some time and experimentation to discover. It's also just a proposal at this time, so it's not ready to build on top of anyway. tl;dr, not sure yet |
|
Thanks for your answer! The experimentation has begun with Reason : https://twitter.com/jaredforsyth/status/1056902656197828608 🎊 EDIT : Another really interesting case (I totally love this one!) : https://paulgray.net/an-alternative-design-for-hooks/ |
|
@jgoux So, I took a stab at it: Counter example with hooks I actually am liking it.. 🤔 I saw that alt design too, and the interesting bit is that it's no different in PS.. do-syntax reads like the React proposal but compiles to the alt proposal 😉 |
|
It feels much more flexible and opt-in (to state and lifecycles), and makes it easier to build complicated state helpers that manage their own subscriptions outside the component. |
|
Wow this API seems awesome! |
|
I removed |
|
Actually, yeah.. it'd make it significantly harder to write custom state/effect logic without |
|
Made a separate PR to discuss hooks: maddie927#1 |
| initialState = | ||
| { counter: 0 | ||
| } | ||
| initialState = { counter: 0, dummy: 0 } |
These changes address a few things:
shouldUpdateandwillUnmountI think this is ready enough for feedback. The API is extremely
reason-reactinspired (Reason has similar language requirements/constraints, the API is very close to vanilla React's besides the concept of Actions/update/reducer because Reason and PureScript have more powerful pattern matching abilities).There are still a few bits I'm not super happy with -- I'll annotate the code to point them out.
This is a pretty significant change over
react-basic2.0. It's now less "basic" in the "implement a small subset of vanilla React in PS, where 'basic' means small" and instead more along the lines of "implement a thin but opinionated wrapper around React, where 'basic' means encourage correctness without sacrificing performance or type complexity". 🙂Remaining work:
Fixes #52
Fixes #53