Totally, my question was more regarding why the usage of labelled arguments altogether now that we have records in place. We could even remove [@react.component] decorator altogether and keep just [@JSX] transformations?
As you well know, JSX ppx works in two parts, definition and application. @react.component is for the definition and @JSX is for the application.
@JSX attribute is used by parser and ppx to transform the JSX expression <Foo /> to the normal function with react api. Actually, the parser parses the JSX expression into the normal function first, then ppx transforms it again with react API such as React.createElement. It is not only a substitution of babel transformation by the compiler, also it is a necessary preprocessing for type checking.
@react.component has more jobs to do. It transforms the labeled function into the function with one argument, the props in the record. And it changes the function name starting with the capital letter, which is valid to react API. The last job is to make the props type for the type checker.
We can write the react function component without @react.component though, but it makes our life a lot easier. V4 transformation is using the record for the props argument, which is more clean and similar to the js representation.
module Component = {
type props = {x:string}
let make = props => React.string(props.x)
}
let _ = <Component x="" />
The one thing it’s missing is the component will not show up as called “Component” while it would if run through the JSX.
This is kind of still in progress. But in future, we might be able to go without @react.component entirely.
We still need backwards compatibility though.
It looks very clean without @react.component now! Component without @react.component is transpiled to the below js output.
function make(props) {
return props.x;
}
var Component = {
make: make
};
...
React.createElement(make, { x: "x" }) <-- make
...
I thought it would not work and throw an error which is complaining about React component function name should start with the capital letter, but it seems fine in my example project. Not sure about it. I’ll look into it too.
Being able to directly write a component like this is great for some cases, but usually I’d rather stay with @react.component so that the compiler can infer the prop types and I do not have to spell them out myself.
So in this example I’d stay with
module Component = {
@react.component
let make = (~x) => React.string(x)
}
But in future, we might be able to go without @react.component entirely.
How could this work without having to spell out the prop types explicitly every time?
So you still need to write the props explicitly and just allow them to be polymorphic? That’s a lot less comfortable than just writing @react.component
module Component = {
type props<'x> = {x:'x}
let make = props => React.string(props.x)
}
Whenever I look this implementation, the expressivity looks quite improved to me, even without @react.component attribute. The empty record type and value would make the expressivity better.
Thanks! Yes, my code for the context provider is like this, and changing to record fixes the problem.
About the new handling of key:
In JSX v3, this used to be zero cost, the key was just included in the props object literal.
In JSX v4 classic mode, there are now helper functions used that will cause additional overhead:
function createElementWithKey(component, props, key) {
return React.createElement(component, Jsx.addKeyProp(props, key));
}
function createElementVariadicWithKey(component, props, elements, key) {
return Caml_splice_call.spliceApply(React.createElement, [
component,
Jsx.addKeyProp(props, key),
elements
]);
}
This does not seem like an improvement over JSX3. At least I think we should inline these calls, and use Js.Obj.assign directly or define Jsx.addKeyProp as an external based on Object.assign. (But the Object.assign itself is already overhead.)
In JSX v4 automatic mode, this problem does not exist, as the key is passed as a separate arg to the jsx function).
I meant not just inlining the Object.assign, but couldn’t we also get rid of the helper function createElementWithKey by having the PPX directly generate