-
Notifications
You must be signed in to change notification settings - Fork 464
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
Feedback from the forum for JSX v4 #5646
Comments
For the type info in the editor, let's first see if adjusting the location of the props type definitio helps. |
@mattdamon108 found out this by chance, a little thing that you might find interesting. This innocent-looking code does not compile with V3: module type OptT = {
module type Opt2 = {
@react.component
let make: (~s: string) => React.element
}
}
module Opt: OptT = {
module Opt2 = {
@react.component
let make = (~s) => React.string(s)
}
module type Opt2 = module type of Opt2
} The reason is that the type for So it's equivalent to this code: module type OptT = {
module type Opt2 = {
@obj external makeProps: (~s: string, ~key: string=?, unit) => {"s": string} = ""
}
}
module Opt: OptT = {
module type Opt2 = {
@obj external makeProps: (~s: 's, ~key: string=?, unit) => {"s": 's} = ""
}
}
// Values do not match:
// external makeProps: (~s: string, ~key: string=?, unit) => {"s": string} =
// "" "#rescript-external"
// is not included in
// external makeProps: (~s: 's, ~key: string=?, unit) => {"s": 's} =
// "" "#rescript-external" However it compilers perfectly fine with V4 because there's no |
It looks really cool. It is due to making the props type as polymorphic and using the type information(string) in the type declaration module type OptT = {
module type Opt2 = {
type props<'s> = {key?: string, s: 's}
let make: React.componentLike<props<string>, React.element> // <- using string type information
}
}
module Opt: OptT = {
module Opt2 = {
type props<'s> = {key?: string, s: 's}
@react.component let make = ({s, _}: props<'s>) => React.string(s)
let make = {
let \"Interesting$Opt$Opt2" = (props: props<_>) => make(props)
\"Interesting$Opt$Opt2"
}
}
module type Opt2 = module type of Opt2
} |
Yes that's precisely why it works. |
@mattdamon108 wondering whether we should revisit Ovservation: is it OK to pass Separately there's the question of what happens when we pass no props (currently |
Thank you for raising the
// original
module Foo = {
@react.component
let make = () => <Bar key="k" x="x" />
}
// V4 + classic
module Foo = {
type props = {key?: string}
@react.component let make = (_: props) => React.createElement(Bar.make, {key: "k", x: "x"})
^^^^^^^^^^^^^^^^^^^
// should be {key: "k", x: "x"}
let make = {
let \"Interesting$Foo" = props => make(props)
\"Interesting$Foo"
}
}
// V4 + automatic
module Foo = {
type props = {key?: string}
@react.component let make = (_: props) => React.jsxKeyed(Bar.make, {x: "x"}, "k")
let make = {
let \"Interesting$Foo" = props => make(props)
\"Interesting$Foo"
}
} In the case of no props, I think we have to pass module Foo = {
type props = {key?: string}
@react.component let make = (_: props) => React.createElement(Bar.make, {key: ?None})
let make = {
let \"Interesting$Foo" = props => make(props)
\"Interesting$Foo"
}
} What do you think? |
Sorry for making confusion. Can you explain little more detail about it? Generally we don't pass the // original
module Bar = {
@react.component
let make = (~x) => React.string(x)
}
module Foo = {
@react.component
let make = () => <Bar x="x" />
}
// converted
module Bar = {
type props<'x> = {key?: string, x: 'x}
...
}
module Foo = {
...
@react.component let make = (_: props) => React.createElement(Bar.make, {x: "x"}) // here
...
} |
My proposal is to explore using key as follows: @react.componet
let make = (~key=?, ~a) => <div ?key> {React.string(a)} </div> If your component wants to use |
|
I'm afraid it would violate the rule of React API, which is that const Foo = ({key, x}) => {
return (<div key> {x} </div>)
} |
The prop |
|
OK so since |
I think the only reason why React.createElement(Bar.make, {key: "k", x: "x"} So we're passing the record to |
So @mattdamon108 all what's needed it to add the |
Ah.. I think I got your point and what you're solving. If I understand correctly, I think you're pointing module Bar = {
type props<'x> = {key?: string, x: 'x}
...
let make = {
let \"Interesting$Bar" = (props: props<_>) => make(props) // <-- 1
\"Interesting$Bar"
}
}
module Foo = {
...
@react.component let make = (_: props) => React.createElement(Bar.make, {key: "k", x: "x"}) // <-- 2
...
} (I've removed unnecessary lines.) @module("react")
external createElement: (component<'props>, 'props) => element = "createElement" Actually, I hope this is right point to what you mean. |
And all react components have the |
FYI, here is the js runtime representation after being transpiled. function Interesting$Bar(props) {
return props.x;
}
var Bar = {
make: Interesting$Bar
};
function Interesting$Foo(props) {
return React.createElement(Interesting$Bar, {
key: "k",
x: "x"
});
}
var Foo = {
make: Interesting$Foo
}; |
Let me try again. You need to pass key, as all components have it. You could have The issue is |
Oh, it makes sense to me. But, still why do we need to get rid of |
It's because of the surprise effect and the boilerplate. If I want to write a component directly, I need to define the key thing for every component. Also, a component with 2 props has 3 fields. Can't think of first field and second field anymore. When hovering, I see the key in the type. When there's an error message, I see the key in the type. When exporting the component to TS, genType needs to explicitly remove key from the props. |
It makes sense to me, now. On second try, I rewrite the let addKey: ('a, string) => 'a = %raw(`
function(props, key) {
return Object.assign(props, {"key": key})
}
`)
module Foo = {
...
let make = (_: props) => React.createElement(Bar.make, addKey(({x: "x"}: Bar.props<string>), "k"))
...
} |
I have another question about declaring the props type of the component which doesn't have any props. Is it possible to declare the empty record type by #5658 ? type props = {} |
One could use an inline function @inline
let addKey = (o: 't, k): 't => Obj.magic(Js.Obj.assign(Obj.magic(o), {"key": k})) or more likely a dedicated |
This is a good question, and I don't have a good answer. |
I guess overall there's no way to pay zero price for |
I think using an empty record instead of the |
Actually the empty record type |
Based on #5658 and addKey function, I think I can try to remove the key from the props. I’m wondering whether it is okay |
In master. |
@mattdamon108 would you explain "Formatting {...str} to spreadProps=str"? |
Currently with JSX v4, the spread props is working only for uppercase. module A = {
@react.component
let make = (~s, ~x) => (s ++ x)->React.string
}
@react.component
let make = () => {
let str: A.props<_> = {
s: "s",
x: "x",
}
<A x="y" {...str} /> // <-- here
} But, the formater is changing Screen.Recording.2022-09-20.at.1.09.40.AM.movIf the formater keeps |
What if a component has a prop called |
Yes, ppx assume that the labelled argument |
Looks like one needs to find some other way of encoding that information. |
This is printing for fragments: | Pexp_construct _ when ParsetreeViewer.hasJsxAttribute e.pexp_attributes ->
printJsxFragment ~customLayout e cmtTbl |
And this is parsing: (*
* jsx-fragment ::=
* | <> </>
* | <> jsx-children </>
*)
and parseJsxFragment p =
let childrenStartPos = p.Parser.startPos in
Scanner.setJsxMode p.scanner;
Parser.expect GreaterThan p;
let _spread, children = parseJsxChildren p in
let childrenEndPos = p.Parser.startPos in
Parser.expect LessThanSlash p;
Parser.expect GreaterThan p;
let loc = mkLoc childrenStartPos childrenEndPos in
makeListExpression loc children None |
So looks like fragments are represented as lists. |
Looks like for example this is not taken: | Pexp_array [spread] when ParsetreeViewer.hasJsxAttribute e.pexp_attributes |
So it could e.g. be encoded as an array of length 1 with a jsx attribute on it. |
Also, where is |
Only 1 would be fine, I think.
If I understand correctly, parsing the |
I would double check that adding more than 1 is a parse error. And if not, make it a parse error.
Yes, and add the jsx attribute too. |
See here: let jsxExpr =
match p.Parser.token with
| Lident _ | Uident _ -> parseJsxOpeningOrSelfClosingElement ~startPos p
| GreaterThan ->
(* fragment: <> foo </> *)
parseJsxFragment p
| _ -> parseJsxName p
in
Parser.eatBreadcrumb p;
{jsxExpr with pexp_attributes = [jsxAttr]}
One code path calls |
Can I look into it and make PR to improve? Parsing and printing. In my opinion, the spreadProps as labelled argument wouldn't bother users too much. If you think it is not an urgent issue, I'd like to fix this, maybe need some time to finish it. |
Not super urgent. |
Not postpone, I'll fix it now. But I'm afraid to take some time, not like you super fast problem solver. Actually I feel like I don't understand the code base around the typecore and printer. I'd like to make dynamic import implementation next, so I need to understand more in depth about printer(js emitter). |
How about I do it? |
Alright you can do it and I can follow what you are going to fix. |
OK turns out my suggestion does not fit, as props need to be (label, expression) pairs. Will do differently. |
Would you take a look rescript-lang/syntax#644 |
What's the last item "Spread props without any other props"? Getting to the point of doing another round of reviews for JSX V4 (after publishing alpha releases). |
Really nice. I'll learn how to printer works from your PR.
Currently, spreadProps is not available to use solely. The spreadProps is used to update the props record. I can fix it soon.
I'll fix "Spread props without any other props" soon and let you know. |
One last thing to do rescript-lang/syntax#645 |
Looks like it's all done now. |
Yes! 👍 |
Feedback from the forum for JSX v4
https://forum.rescript-lang.org/t/call-for-help-test-the-react-jsx-v4/3713
Fixes
{...str}
tospreadProps=str
key
to the component in V4 + classic Remove key prop from JSX props type syntax#635 Fix type error byJsx.addKeyProp
for the empty props syntax#641Improvements
classic
andautomatic
meansmemo
Compiler@master sometimes reports module name instead of source file as error location #5635The text was updated successfully, but these errors were encountered: