-
Notifications
You must be signed in to change notification settings - Fork 450
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
Explore to make JSX component abstract #6304
base: master
Are you sure you want to change the base?
Conversation
@@ -7,11 +7,15 @@ module V4A = { | |||
let make = (type a, ~a: a, ~b: array<option<[#Foo(a)]>>, ~c: 'a, _) => React.null | |||
} | |||
|
|||
let _ = <V4A a="a" b=[] c=1 /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Withouth the component application, The type of this module contains type variables that cannot be generalized:
A build error occurred.
// @react.component | ||
// let rec make = (~foo, ()) => React.createElement(make, makeProps(~foo, ())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also modify JSX3?
Awesome! Thanks! Will test tomorrow. |
@cknitt Thanks! I'll keep going through the test cases and see if I can reduce the breaking changes. |
@@ -36,7 +36,7 @@ external string: string => element = "%identity" | |||
external array: array<element> => element = "%identity" | |||
|
|||
type componentLike<'props, 'return> = 'props => 'return | |||
type component<'props> = componentLike<'props, element> | |||
type component<'props> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change will eventually affect JSX3 as well.
@mununki I tried to build one of our projects (fairly large, web + React Native) with this change. And actually this didn't look too bad. All dependencies built successfully without any changes/patching. There were some issues in the project code itself though. I had little time today and haven't finished resolving all of them, but I have not come across a blocker yet. Will continue tomorrow. One interesting thing was this: @react.component
let make = (~x) =>
switch x {
| #a => React.string("A")
| #b => React.string("B")
| _ => React.string("other")
} =>
|
Yes, that is one of things to be fixed in this PR. The trasformation of component definition is changed in this PR as below: // before
type props<'x> = {x: 'x}
let make = ({x, _}: props<_>) =>
switch x {
| #a => React.string("A")
| #b => React.string("B")
| _ => React.string("other")
}
let make = {
let \"Abstract" = (props: props<_>) => make(props)
\"Abstract"
}
// after
type props<'x> = {x: 'x}
let make = ({x, _}: props<_>) =>
switch x {
| #a => React.string("A")
| #b => React.string("B")
| _ => React.string("other")
}
let make = {
let \"Abstract" = React.component((props: props<_>) => make(props)) // wrapping with React.component
\"Abstract"
} This change seems to interfere with the compiler's type inference. There are a couple other cases that I'm looking at. |
The same thing occurs whenever there is a type variable in a prop type like in @react.component
let make = (~x: 'x, ~render: 'x => React.element) => render(x) I have something similar in one component, so that's blocking me now. |
Is there a way to approach this gradually? |
Well, yes, but usually you will be using bindings to 3rd party libs when working with React (or writing bindings yourself), so you will be using FFI. Even with just @react.component
let make = () => {
React.Fragment.make({children: React.string("Boom")})
} would compile fine and go 💥 at runtime. Maybe a contrived example, but still. |
Asking in case one can special case the ffi only. |
785bc59
to
7af7bba
Compare
You you can't do type inference under an application. let make = {
let \"Abstract" = (props: props<_>) => make(props)
React.component(\"Abstract")
} |
Wow! Thanks! I'll change and try it. |
If I change the application location of the module C = {
@react.component
let make = (~x: 'x, ~render: 'x => React.element) => render(x)
}
// adding the component application, then build fine no matter `React.component` locations.
let _ = <C x=1 render={v => React.int(v)} /> |
EDIT: Realize I posted in the wrong PR... |
7af7bba
to
d504afa
Compare
@cknitt Off the top of my head, if we were to make the type of the react component abstract, so that we could use the React.component identity function via ppx only to define the component type, we'd have one problem. We won't be able to define components like this without ppx. module C = {
type props = {a: string}
let make = props => React.string(props.a)
} So why don't we move towards making |
d504afa
to
c738f67
Compare
Rebased to master |
I've discussed this issue with @cristianoc, we can summarize the issue to the example below: type t<'a> = [> #a | #b] as 'a
let f = (x: t<_>) =>
switch x {
| #a => React.int(0)
| #b => React.int(1)
| _ => React.int(2)
}
let component: ('props => 'element) => 'component = a => a
let \"H1" = component((x: t<_>) => f(x)) // error
let \"H2" = Obj.magic((x: t<_>) => f(x)) // Ok The |
discussion: #5725
This PR explores to make the JSX component as an abstract type. As a result of this PR change, the following type definition will cause a build error.
And now components that don't return
React.element
will throw a build error.Note: With this change in PR, it looks like we're going to have to rewrite quite a bit of our tests. For now, I've commented out tests like
/built_test/react_ppx
for exploration.