facebook / jsx

The JSX specification is a XML-like syntax extension to ECMAScript.
http://facebook.github.io/jsx/
1.96k stars 133 forks source link

support object literal property value shorthand #23

Open bloodyowl opened 9 years ago

bloodyowl commented 9 years ago

side note : following the issue facebook/react/issues/2536

what I'd like to be able to do with jsx is this :

{this.state.list.map((item, key) => <MyComponent {item, key} />)}

I know it's not to be considered a big syntax issue, its just sugar, but it seems logical to me that after supporting the spread operator, as this other ES6 feature is supported by jstransform/jsx

as @sebmarkbage said in the related react issue, the <Foo item /> syntax is supported for boolean attributes, but IMO

var item = "value"
return <Foo {item}/>

isn't that confusing as it is going to get supported by various browsers soon, and developers will have to know it.

so now, I think it's all about discussing this proposal :smile:

RReverser commented 9 years ago

what I'd like to be able to do with jsx is this :

{this.state.list.map((item, key) => <MyComponent {item, key} />)}

Well, you can do it like following:

{this.state.list.map((item, key) => <MyComponent {...{item, key}} />)}
bloodyowl commented 9 years ago

this doesn't seem to work so far

RReverser commented 9 years ago

You probably didn't enable ES6 transformation in your transpiler.

bloodyowl commented 9 years ago

oh, sorry, I actually did for this test.

but is it ~really~ logical that {...{foo}} is supported when {foo} isn't?

RReverser commented 9 years ago

Tough question. If React would start supporting {foo}, {foo, bar}, then it would be logical to support {foo: 1}, {foo: 1, bar: function () {}} as well and finally we would just have object literals following tag name. Current spread-like syntax looks more logical.

ghost commented 9 years ago

Thank you for reporting this issue and appreciate your patience. We've notified the core team for an update on this issue. We're looking for a response within the next 30 days or the issue may be closed.

denvned commented 8 years ago

I agree with @bloodyowl in that allowing spread-like syntax and not allowing object literals is not so logical.

But if not object literals then at least <Comp {propA} {propB} {propC}> should be allowed as a shorthand for <Comp propA={propA} propB={propB} propC={propC}>, because it is way easier to read and write.

wmertens commented 8 years ago

Right now the spec is that JSXSpreadAttribute is the first attribute and is of the form { ... AssignmentExpression }.

If instead it was of the form { es6ObjectLiteral } it would be equally specific and parseable, and it would make tedious passing down of individual attributes a lot nicer…

Note that ES6 syntax has been around for a while now and used a lot, and the fact that {...props} is not an actual ES6 object literal expression is just plain confusing.

RReverser commented 8 years ago

Right now the spec is that JSXSpreadAttribute is the first attribute and is of the form { ... AssignmentExpression }.

It's not necessarily the first.

wmertens commented 8 years ago

Oh you're right, I didn't see the ending s on the recursive attribute definition.

All the better, that's nicer to use and it doesn't change the specific parseability, the only thing in a tag that starts with a { is a spreadattribute.

It still looks exactly like an object literal but it sadly and unnecessarily isn't.

wmertens commented 8 years ago

I keep wishing for this, every time I write intermediate components that just pass props:

<MyComponent {...{key: bar, prop1, prop2}}/>

just seems uglier than

<MyComponent {key: bar, prop1, prop2}/>

Supporting the latter syntax is essentially free, as the former syntax is just a special case of the latter. There are no parsing conflicts, no transpiling inefficiencies, … it just looks and behaves like ES6 and thus reduces astonishment.

jonvuri commented 8 years ago

First of all, the object spread operator actually isn't in "es2015" preset proper, but still in stage 2. For teams like mine where we prefer to stick to the established standard for guaranteed maintainable code, the spread operator isn't an option for us yet, and this syntax excludes us.

But also, it really does not make sense for {...{foo, bar}} to work and for {foo, bar} not to. They both end up being {foo, bar} after being evaluated, and if you passed them to any normal function, that function wouldn't (and shouldn't) know how it got evaluated.

JSX is already a series of keys and values, why can't those keys and values be expressed in a normal inline object literal? That would make perfect sense.

merlinstardust commented 8 years ago

Would it at all be possible / make sense to instead have this done without the object literal but still use the idea of shorthand? By that I mean, you could do this

const a = 1;
const b = 2;
...
<Component a b>

I know that currently this actually passes in true to both a and b but I don't know why that's the case.

johnste commented 8 years ago

@merlinpatt I think there's a big risk of confusion to do it that way. If you add a property c which is undefined, does that still mean it would be recieved as true? Property names would take of different meaning depending on the contents of the scope they are in.

wmertens commented 8 years ago

…so, is there any chance of this ever happening? What would it take, a petition, a funny youtube video, Belgian chocolate?

syranide commented 8 years ago

My perspective on this:

<Foo {item, key}/> (as sugar for <Foo item={item} .../>) seems kind of quirky, but <Foo {item} {key}/> seems reasonably useful for the same reasons as {item, key} was added to ES6. But the syntax is really weird, {} implies an expression but the syntax actually expects an identifier name. <Foo item key /> makes sense in context but that's already taken by boolean-true props. There's also computed property names to consider, <Foo [item] [key]/> should realistically set them to true, which conflicts with how this new syntax would work, which makes understanding the behaviors of these similar looking syntaxes non-trivial.

<MyComponent {...object}/> that we have now makes sense to allow dynamically adding props, especially as you should be able to do <MyComponent {...object1} foobar {...object2}/>. However, <MyComponent {object}/> should explicitly prevent any other props from being added, so the only purpose for that syntax is <MyComponent {foo, bar}/> (two different but equal syntaxes, that's not a good thing) or because you don't like how <MyComponent {...object}/> looks (... is about conveying consistent behavior, that's a good thing). There's nothing obviously beneficial here.

jonvuri commented 8 years ago

<Foo {item, key}/> (as sugar for <Foo item={item} .../>) seems kind of quirky

Why do you feel like it's quirky, @syranide? What if we focus less on this case of property shorthand as a sugar and just talk about this idea: Any object literal expression in JSX sets its key-values as attributes on the element, without worrying about the inner syntax of the literal. We can even think of it as if the object literal attribute syntax is the "natural" JSX syntax since it is more expressive, with the HTML-attribute syntax being the sugar instead:

<MyComponent { prop1: 1, prop2: 'foo' } /> <MyComponent prop1={ 1 } prop2='foo' />

I feel like anyone familiar with JSX wouldn't blink much at the above, and would understand the intent and how it should behave. It also wouldn't be very surprising to then see things like this and understand what's going on:

<MyComponent { [ 'prop' + propIndex ]: 'foo', prop2 } />

And the spread operator is even still supported, with the same semantics:

<MyComponent { ... propList1, ... propList2 } />

( And as a note about your example <MyComponent {...object1} foobar {...object2}/>, there is precedent for properties being specified more than once - if you do that in a normal object literal the last one wins:

{ foo: 1, bar: 2, bar: 3} -> { foo: 1, bar: 3 }

So if we imagine all object literals in one JSX element as just being "concatenated" these would act the same way, with left-to-right merging:

<MyComponent { ... propList1 } { ... propList2 } /> <MyComponent { ... propList1, ... propList2 } /> )

In comparison with this syntax, the current spread-operator syntax is the one that seems odd and less obvious. It's unexpected that these two expressions are not equivalent, when they are equivalent elsewhere in ES2015:

{ foo: 1, bar: 2 } { ... { foo: 1, bar: 2 } }

JSX peeks into the object literal expression and uses its spread operator to instead mean "spread attributes over the JSX element" rather than spreading properties over the object literal, and that behavior isn't very obvious without a prior explanation. And even if it made sense, again, the spread operator is nonstandard in ES2015 and still subject to change, and requires an extra plugin to work.

Also, I'm not sure I understand this, @syranide:

However, <MyComponent {object}/> should explicitly prevent any other props from being added

Why? Why couldn't you mix object expressions and normal attributes? All I said above is assuming that should be possible but perhaps I'm missing something.

syranide commented 8 years ago

@jrajav I was commenting on the two different ideas presented separately.

<MyComponent { prop1: 1, prop2: 'foo' } />
<MyComponent prop1={ 1 } prop2='foo' />

Anyway, both are fine, I even proposed switching to the first one early on, IMHO the most important thing about JSX is that it adds visual clarity to hierarchies, not that it looks like XML. Reusing the native object literal syntax makes a ton of sense technically. However, having both doesn't really make sense. A language isn't about having multiple ways to do the exact same thing based on preference, it's about having a common language to "speak".

Also, worth noting that the object literal syntax may not lend itself as well to spanning multiple lines and in a sense it's also kind of illogical why the braces are in the syntax to begin with, they aren't necessary for anything, they just become fluff from a technical perspective (but that's a separate discussion and there may be reasons why it makes sense):

<MyComponent {
  prop1: 1,
  prop2: 'foo'
} />
<MyComponent
  prop1={ 1 }
  prop2='foo'
/>

I think even the spread syntax that JSX has now is kind of weird, but it's probably the only really logical syntax for it given the current syntax of JSX. If <Foo bar/> was actually an alias for <Foo bar={bar}/> then <Foo ...object/> would make sense for the spread syntax and JSX would feel more coherent.

In comparison with this syntax, the current spread-operator syntax is the one that seems odd and less obvious. It's unexpected that these two expressions are not equivalent, when they are equivalent elsewhere in ES2015:

If the syntax was <Foo bar=[1]/> instead then the spread syntax IMHO would be <Foo [...object]/>, hence {} is about expressions and not object literals (but that's debatable). Anyway, so <Foo {...foo} bar={1}/> is currently translated {...foo, bar: 1}, i.e. <Foo {#} bar={1}/> translates to {#, bar: 1}. Now you can extend this with multiple values and key-value pairs too as you're suggesting, but we already support both of those features with the current JSX syntax, so we shouldn't add another syntax for it where the only reason to pick one over the other comes down to personal preference = not a good feature to add.

In summary, I largely agree that the object literal syntax is preferable in many ways, practically and technically, but that syntax explicitly competes with a syntax already decided on by JSX. It makes sense having competing languages implementing alternative syntaxes. It does not make sense having it in JSX, JSX is an explicit choice with an opinionated syntax. I can also imagine a few additional tweaks to the syntax that would further make it less XMLy and more JSy, which may make sense given that HTML is far from the only frontend out there.

PS. This is my opinion/perspective and may not be shared by everyone else.

jonvuri commented 8 years ago

@syranide A lot of what you're saying makes sense and I can see where you're coming from. I do support the idea of having one way to do something, where it makes sense.

Just to reiterate what my main pain point is, it's less to do with the spread operator that you focused on (which I honestly don't care much about, I prefer explicitly passing each property for a robust interface). It is more that there isn't a non-ugly way to apply local symbols to an element without repetition. There are only two alternatives:

<MyComponent foo={ foo } bar={ bar } /> Repetition <MyComponent { ... { foo, bar } } /> Ugly, stage 2, requires explanation

Allowing arbitrary object literal expressions would solve this neatly and also enable other useful patterns like computed keys.

As for the notion of competing syntaxes, that's one way to look at it. But another way to look at it as JavaScript vs. the JSX sugar for the JavaScript. Elsewhere in JSX, { } means "Exit back to JavaScript here and compose with it in a sensible way." Couldn't it mean the same thing in this case? Allow JavaScript interpolation for props as well and not just children, with the resulting object value being concatenated at that point in the eventual props object. Basically, just a way to have more direct access to the props object inline in JSX. And because it's plain JavaScript, it's more powerful and expressive. JSX composing so well with JavaScript is one of its main strengths, but attribute lists are one of the missing pieces currently, when they could easily benefit from the same. And from this perspective, the key='value' syntax for attributes is just a sugar for adding props to that object with more HTML-like semantics, not a competing syntax, the same way that there's no competition for a <div>{ list.map(() => { ... }) }</div> because JSX lacks the expressiveness for it.

Granted, this would make more sense if the syntax was instead <MyComponent { { foo: 1, bar: 2 } } />, but that would still be an improvement in my opinion.

syranide commented 8 years ago

It is more that there isn't a non-ugly way to apply local symbols to an element without repetition. There are only two alternatives:

@jrajav Yeah, if that's what you want then spread really isn't a good fit, no doubt. But I'd say the proposed syntax seems like an overreach for trying to accomplish just that. So in my opinion, as I said, I think the real solution would be to remove boolean-true props i.e. no more <Foo thisistrue/>, that would allow us to provide an element prop syntax that is functionally on par with object literals. I personally would probably find that preferable, but I imagine that not everyone agrees, especially considering the current legacy (although that is codemod-able). It comes down to choosing which of the two features you want, it seems you can't have both and have a truly familiar and coherent syntax. Both aren't a necessity either, languages have strengths and weaknesses, it's about balance.

Going off on a tangent, if we imagine a future where ECMA wants to incorporate these "elements" into JS, then it's important to remember that they may chose a very different version of JSX. The JSX we're discussing here is simply one possible proposal in that scenario. But if this JSX were to be seriously considered it also has to be "rigorously designed", adding small nice-to-have features with large-scope syntax changes/side-effects seems like a quick way to shut that door, there needs to be a coherent vision for the language, that includes a balanced trade-off.

Elsewhere in JSX, { } means "Exit back to JavaScript here and compose with it in a sensible way." Couldn't it mean the same thing in this case?

IMHO, that's the wrong way to look at it, "JSX-elements" is a true citizen of this language, there is no exiting back to JS. Expression values can be JSX-elements and JSX-element property values can be expressions. JSX-elements is part of the language grammar like everything else, it's not special, just more syntax rules.

Put differently, if I would design JSX I would design the syntax to more along the lines of the following, to be more comparable to JS syntax.

<Foo
  booleanValue=true
  numericValue=1
  stringValue="bar"
  valueByPropName
  ...spreadObject
/>

My overall point is; consider if you made the same syntax extension to the current JS object literal syntax, adding a separate but equivalent syntax to achieve almost exactly the same thing and a nice-to-have-feature. It doesn't seem like a good idea.

wmertens commented 7 years ago

@syranide I agree with you that the syntax should not become overly sugary and complex, but our point is that the current syntax is a subset of the proposed syntax, given the stage-2-but-ready-for-stage-3 object spread operator.

That is to say, <MyComponent { ... { foo, bar } } /> behaves exactly the same in this proposal, but <MyComponent { foo, bar } /> becomes possible, using language constructs that are already solidly in place.

Furthermore, adding JSX syntax to ECMA seems improbable, and if it happens in has only 2 outcomes:

  1. The syntax is JSX => whatever we have then is the standard
  2. The syntax is not JSX => we will need to transpile like before

So I don't think this possibility should inform this decision.

For me it is clear-cut: Make JSX more obvious at 0 cost, or not.

jonvuri commented 7 years ago

Yeah @wmertens, that's still a pertinent point - again, it would make a lot of sense to simply support any sort of object literal expression since that's what the current syntax looks like anyway and it would be perfectly compatible with it. Nothing crazy is even being added, just making the current implementation make more sense.

And as for trying to do this in the "native" property-value syntax as you're proposing @syranide, I just want to point out that that would interfere slightly with applying attributes to the elements, for instance disabled, in the same way you'd apply them to native HTML elements, if you are using the same name in scope. With the object literal syntax it would be more obvious what the intent is, and would reduce the risk and annoyance of possible shadowing.

denvned commented 7 years ago

Put differently, if I would design JSX I would design the syntax to more along the lines of the following, to be more comparable to JS syntax.

<Foo
  booleanValue=true
  numericValue=1
  stringValue="bar"
  valueByPropName
  ...spreadObject
/>

As @jrajav pointed out, someone with a background in HTML would expect valueByPropName to be a truthy boolean here...

I think the problem here is that JSX too much tries to look like HTML. If it were more obvious that JSX is more JS rather than HTML... For example (note colons and commas):

<Foo
  booleanValue: true,
  numericValue: 1,
  stringValue: "bar",
  exprValue: x + y,
  valueByPropName,
  ...spreadObject
/>

The only problem I can see with the above syntax is that the closing bracket > might be confused with the greater than operator: <Foo booleanValue: bar>baz></Foo>, but the parser probably could raise an error in such case, and the user could fix it with extra (): <Foo booleanValue: (bar>baz)></Foo>, or a trailing comma: <Foo booleanValue: bar,>baz></Foo>. The fix is ugly, but probably would not be needed often. with the named character entity gt: <Foo booleanValue: bar>baz&gt;</Foo>.

jonvuri commented 7 years ago

Not much input on this for a while... would a pull request be welcome here?

wmertens commented 7 years ago

The problem here is that one side (contra this issue) believes that {[object literal]} confuses the syntax and {...{[object literal}} is good enough, and the other side (pro) can only point to a stage-3 ES2017 feature (object spread operator) and say it's more logical. So it's pragmaticism vs purity.

It's not a missing PR issue, we have the technology.

On Wed, Oct 12, 2016 at 8:44 PM Jonathan Rajavuori notifications@github.com wrote:

Not much input on this for a while... would a pull request be welcome here?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/jsx/issues/23#issuecomment-253301931, or mute the thread https://github.com/notifications/unsubscribe-auth/AADWlg3bgu-9c8_CykTBXArSe_Trh1T7ks5qzSqFgaJpZM4DBkOp .

sebmarkbage commented 7 years ago

I'm in general supportive of this general feature. I think we should probably just remap the meaning of <Foo x />. See #65

spudly commented 7 years ago

What do you think of limiting the scope to a single identifier inside curly braces ({myProp}) that is equivalent to myProp={myProp}?

For example:

<Comp {...obj} {value} flag />; // same as <Comp {...obj} value={value} flag={true} />

Which would become...

React.createElement(Comp, _extends({}, obj, { value: value }, { flag: true }));

If you want to specify multiple, do them in separate curly brace instances:

<Comp {a, b} c="c" /> // invalid syntax
<Comp {a} {b} c="c" /> // :)

If you need to rename the prop, just do so using already-existing jsx syntax:

<Comp {a: aWithAnotherName} /> // invalid syntax
<Comp aWithAnotherName={a} /> // :)

If I'm not mistaken, this would not be a breaking change.

zinkkrysty commented 7 years ago

Here I have to agree with @jrajav , as the most logical for one such as me (with relatively little experience in react) would be to write

<Comp {{ propName, otherProp: 2 }} /> 

Since it is following the conventions of every other interaction between JS and JSX. The first curly braces mean here begins JS code, and I want to output an object literal, which can be used to build the properties that are passed to Comp. This also means that

<Comp { ...otherProps } />

should be deprecated (but still supported for backward-compatibility) in favor of

<Comp {{ ...otherProps }} />

Which is tiny bit more verbose but follows the convention and is easy to parse (as it is just a JS expression).

pkrawc commented 7 years ago

@zinkkrysty Your second example would wrap the spread with an object and cause breaking changes and an unnecessary flatten. @spudly's syntax would indeed produce no real change and just be an extension to the underlying api

garkin commented 7 years ago

Event handlers would be glad to get some love too. I suggest <button {this.onClick}/> to be equal to <button onClick={this.onClick}/>. And <button {this.props.onClick}/> to be equal to <button onClick={this.props.onClick}/>.

wmertens commented 7 years ago

@garkin neither is valid object shorthand syntax, and it's also counter to the convention of naming handlers for onX to be handleX… You could do

const {handleClick:onClick} = this
…
<button {onClick} />

On Thu, Aug 3, 2017, 6:49 AM garkin notifications@github.com wrote:

Event handlers would be glad to get some love too. I suggest <button {this.onClick}/> to be equal to <button onClick={this.onClick}/>. And <button {this.props.onClick}/> to be equal to <button onClick={this.props.onClick}/>.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/jsx/issues/23#issuecomment-319866961, or mute the thread https://github.com/notifications/unsubscribe-auth/AADWlkBRorhfOpssUO-Mo1gcughOBKrTks5sUVFKgaJpZM4DBkOp .

endash commented 7 years ago

The way I see it, the larger part of the issue is that one aspect of the syntax (<Component property />) is harmonized with expectations about how HTML works, combined with how, in general, JSX apes HTML enough that it doesn't feel out of place at all, and the other part of the syntax appears to be harmonized with expectations about how ES* works, but that is in actuality only spread-deep, and seems to be so to avoid even more ambiguous possibilities, rather than for any actual virtue of its own.

{...props} in-and-of-itself doesn't make a whole lot of sense. We use that syntax in JS to get a new object, but in JSX it isn't actually treated as JS nor does it behave as JS. {...props, anotherProp} is invalid, for instance. There's also the problem of double braces, or the lack there-of. prop={{}} will pass an empty object, for instance, where prop={} won't, so it kind of looks like JSX and it kind of looks like JS, but only for lack of a better, more JSX-y alternative.

I, personally, believe it's natural and commonplace to see it as JS even if that isn't entirely consistent with the behavior or with how braces are used elsewhere. The problem, IMO, ultimately is the lack of self-consistency and failure to meet expectations of such, as much as simply not supporting a given use case. The weird status-quo lets both camps see in the current syntax that which matches their own personal preferences, but the ambiguity that allows that should be an indication that there is actually a problem, rather than merely a difference of opinion. You can see it as "obviously not JS" because of the behavior and use of braces or whatever reason, and you can just as easily see it as "close enough to JS" that it trips your expectations from 0 to 100 even when it's only giving you 10%, because it isn't unambiguously JSX, either.

As part of the latter group, it seems obvious to me that harmonizing this particular syntax with JS object literals solves a number of problems:

1) It simplifies JSX in general (if not in the actual implementation) by removing the custom spread behavior 2) It solves the perennial problem of wanting a shorthand for property={property} 3) It allows for the natural extension of the spread syntax to other standard behavior, like {...props, ...options} or similar

My preference for syntax, as I believe it wouldn't be a breaking change in any sense (update: ES5 isn't a concern, right?), is just to allow a JSX-braces-less object literal in place of {...props}, such that the following would be valid:

<Component {...props} />
<Component {...props, ...options} />
<Component {...props, anotherProp} />
<Component {...props, anotherProp: "Foobar"} />
<Component {foo, bar, foobar} />

And so on. In every case, the object literal transfers directly to the props object, rather than translating into some intermediate JSX property semantics.

The main drawback as I see it is that the harmonization with JS is only object-literal-deep. Some might expect that you could use a reference to an object, e.g. <Component options />, but that would just get them the HTML-esque property behavior. Another drawback is the possibility that the allowance in this case of a bare object literal will further confuse new devs as to the syntax for passing object literals as properties, where the wrapping braces would still be required. My answer to that would be to treat this shorthand as advanced behavior, and simply not surface it in contexts that might be confusing or that are oriented toward new users.

The other alternative I would seriously consider is <Component {props} />. Then you could do <Component {{prop1, prop2, prop3}} />, and so on. I would be happy with either possibility. The current behavior, though, is just maddeningly incomplete.

Contrary to @pkrawc, and to @zinkkrysty's credit, I believe this could be non-breaking, since a bare spread operator would be invalid syntax, so old-style {...props} could unambiguously be differentiated from {{...props}}. At the very least, there's a sensible deprecation mechanism.

wmertens commented 7 years ago

I'm with you about the confusing and the two camps each seeing their own syntax beleaguered, but I lost you in your conclusion. What are you supporting? <Component {_js_objectliteral} /> or something else?

On Fri, Aug 11, 2017 at 5:12 PM Christopher Swasey notifications@github.com wrote:

The way I see it, the larger part of the issue is that one aspect of the syntax is harmonized with expectations about how HTML works, combined with how, in general, JSX apes HTML enough that it doesn't feel out of place at all, and the other part of the syntax appears to be harmonized with expectations about how ES* works, but that is in actuality only splat-deep, and seems to be so to avoid even more ambiguous possibilities, rather than for any actual virtue of its own.

{...props} in-and-of-itself doesn't make a whole lot of sense. We use that syntax in JS to get a new object, but in JSX it isn't actually treated as JS nor does it behave as JS. {...props, anotherProp} is invalid, for instance. There's also the problem of double braces, or the lack there-of. prop={{}} will pass an empty object, for instance, where prop={} won't, so it kind of looks like JSX and it kind of looks like JS, but only for lack of a better, more JSX-y, alternative.

I, personally, believe it's natural and commonplace to see it as JS even if that isn't entirely consistent with the behavior or with how braces are used elsewhere. The problem, IMO, ultimately is the lack of self-consistency and failure to meet expectations of such, as much as simply not supporting a given use case. The weird status-quo lets both camps see in the current syntax that which matches their own personal preferences, but the ambiguity that allows that should be an indication that there is actually a problem, rather than merely a difference of opinion. You can see it as "obviously not JS" because of the behavior and use of braces or whatever reason, and you can just as easily see it as "close enough to JS" that it trips your expectations from 0 to 100 even when it's only giving you 10%, because it isn't unambiguously JSX, either.

As part of the latter group, it seems obvious to me that harmonizing this particular syntax with how JS solves a number of problems:

  1. It simplifies JSX in general (if not in the actual implementation) by removing the custom splat behavior, or at least by appearing to do so, to the user.
  2. It solves the perennial problem of wanting a property shorthand property={property}
  3. It allows for the natural extension of the splat syntax to other standard behavior, like {...props, ...options} or similar

The main drawback as I see it is that the harmonization with JS is only object-literal-deep. Some might expect that you could use a reference to an object, e.g. , but that would just get them the HTML-esque property behavior.

The other alternative I would seriously consider is <Component {props} />. Then you could do <Component {{prop1, prop2, prop3}} />. I would be happy with either possibility. The current behavior, though, is just maddeningly incomplete.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/jsx/issues/23#issuecomment-321840219, or mute the thread https://github.com/notifications/unsubscribe-auth/AADWlpLJbcpWCK5pwHNCRFioWqTtKbMHks5sXG9OgaJpZM4DBkOp .

endash commented 6 years ago

@wmertens

As I read it there are three main, more-or-less mutually exclusive options in play:

<Component {prop} />                // as shorthand for <Component prop={prop} />
<Component {...obj, key: val} />    // object literal, no wrapping curly braces
<Component {{...obj, key:val}} />   // object literal, wrapping curly braces, makes
                                    // <Component {props} /> possible

I'm in favor of either object literal syntax, with or without curly braces, whichever makes the most sense in terms of backward compatibility and any associated carrying costs. The first option (individual property shorthands) IMO just furthers the API confusion, taking into account the existing HTML-like behavior of a bare property equating to true, and the continuing existence of the quasi-JS-literal spread syntax.

wmertens commented 6 years ago

@endash actually, your first two examples are the same, just object literal syntax compatible with the current spread syntax, while the third example is different.

<Component {prop} />                // React.createElement(Component, {prop})
<Component {...obj, key: val} />    // React.createElement(Component, {...obj, key: val})
<Component {...obj} />              // React.createElement(Component, obj)

The third example is confusing:

<Component {{...obj, key:val}} />   // React.createElement(Component, {...obj, key: val})
<Component {props} />               // ??? {...props} or {props: props}?
endash commented 6 years ago

@wmertens The first example would also be valid syntax in the case of the second option. The reverse isn't true, if {prop} translates to prop={prop}, a new shorthand feature existing alongside the current spread operator.

So, they're each distinct options and I'm in support of either of the two that support full JS object literal syntax. I don't care about extra curly braces, personally, and would be fine with or without.

Re: the fourth example. If you can have a set of standalone curly braces encompassing an object literal for the props, then it stands to reason the same syntax can unambiguously support a reference to an object, as well.

var props = {a: 1, b: 2};
<Component {props} />
<Component {{a: 1, b: 2}} />
<Component {{...props}} />

would all be (non-referentially) equivalent. It would all boil down to supporting a single set of "floating" curly braces, inside which is an object or reference to an object.

ngryman commented 6 years ago

Hey, can't we just go with @spudly's proposal?

It would just add a shorthand syntax, end of story. Playing with spreading attributes might be a dangerous game at the end.

wmertens commented 6 years ago

Hmmm. I would certainly be happier than having to write lots of duplicate keys and values, and <Foo {...props} {bar} {...extraProps}/> is not that far from <Foo {...props, bar, ...extraProps}/>, but it would still be a little unexpected that that latter won't work. Also, it opens the door towards the syntax in the latter example, since it's just a subset.

On Sun, Sep 10, 2017 at 11:14 AM Nicolas Gryman notifications@github.com wrote:

Hey, can't we just go with @spudly https://github.com/spudly's proposal https://github.com/facebook/jsx/issues/23#issuecomment-291850564?

It would just add a shorthand syntax, end of story. Playing with spreading attributes might be a dangerous game at the end.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/jsx/issues/23#issuecomment-328330042, or mute the thread https://github.com/notifications/unsubscribe-auth/AADWltVssS_fo0nIfmfPIQmZ6aRqKi3Cks5sg6h2gaJpZM4DBkOp .

jonvuri commented 6 years ago

Will this see any more movement? Seems to have stagnated a bit but I feel the lack of this with every big JSX expression I write.

AndersDJohnson commented 6 years ago

We really need this. I came to similar thoughts on my own a while back: https://github.com/AndersDJohnson/rfc-jsx-attribute-shorthand

I am in favor of the arbitrary object literal syntax and I think the double braces have the least ambiguous semantics (it's simply an expression that evaluates to any object of key/value pairs).

const a = 'one'
const b = 2
const props = { a, b }

<Component {{a}} />
<Component {{a, b}} />
<Component {props} />
<Component {{...props}} />
<Component {{ [a]: b }} />

I also considered an expressionless prop with an equals sign:

<Component a= b= />

P.S. @garkin for your event handler idea. I have thought of something like that before as well (https://github.com/AndersDJohnson/rfc-js-object-shorthand-path). If we get this prop object syntax since we should get all features of JS object syntax, so maybe someday we could combine this with that.

ArcanoxDragon commented 6 years ago

I know this issue hasn't had much activity recently but this is a feature I keep longing for every time I write React code. I thought I'd at least propose the syntax that I originally thought of when I was looking for this feature, which is to recycle the @ symbol used by decorators and allow property shorthand as such:

render() {
    let { value } = this.state;

    return <input className={"form-control"} @value />;
}

In the context of a decorator, @identifier target is interpreted as "apply the function referenced by identifier as a decorator to target", so it would make sense for <Component @attribute /> to be interpreted as "apply the variable referenced by attribute as an attribute to Component".

I'm not aware of any other conflicting interpretation of the @ sign in the context of a JSX element tag. Just my two cents.

ljharb commented 6 years ago

Decorators can appear in the JS contexts inside jsx; i think that would be confusing to “recycle” anything.

ArcanoxDragon commented 6 years ago

How about <input className={"form-control"} :value />? I don't believe that semantic would interfere with any other similar ones.

dantman commented 6 years ago

I'm not aware of any other conflicting interpretation of the @ sign in the context of a JSX element tag. Just my two cents.

How about <input className={"form-control"} :value />? I don't believe that semantic would interfere with any other similar ones.

Both of those are one of the proposals for custom attributes vs custom properties. With pre-existing examples of other template syntaxes that have already given those symbols in that context meanings that developers may mistake them for when they switch between those other libraries and React.

Also I don't think "apply the variable referenced by attribute as an attribute to Component" makes sense as a mapping from decorators. That's basically rewriting 2/3 of what a decorator does, it's not a smooth mapping at all. It would make much more sense if someone suggested that in <custom-img @attribute width="100%"> the @attribute would be a decorator applied to width="100%"

jakeNiemiec commented 6 years ago

How about <input className={"form-control"} :value />? I don't believe that semantic would interfere with any other similar ones.

This would be familiar to anyone who has used hash keys from Ruby.

dantman commented 6 years ago

This would be familiar to anyone who has used hash keys from Ruby.

I don't see how, :value is not syntax that references the variable value. It's syntax for creating a symbol. h = { :foo => 'bar' } is roughly equivalent to const foo = Symbol('foo'); h = { [foo]: 'bar' }. And h.fetch(:foo) is just a request to get the object value for the key :foo.

endash commented 6 years ago

I still would prefer full-object-literal support somehow, but for individual props how about surrounding it in brackets? ~This would be (superficially, admittedly) similar to how variables can be used to define keys in object literals~ Actually on second thought they're not really similar at all. Anyway:

<input className="form-control" [value] />
ArcanoxDragon commented 6 years ago

If we're adhering to ES6 semantics, I would actually expect that to take the string value of the variable called value and use that as the key with which to set an attribute:

let value = "className";
let otherValue = "disabled";

<input [value]={ "form-control" } [otherValue] />

output:

<input class="form-control" disabled="disabled" />
dantman commented 6 years ago

If we're adhering to ES6 semantics, I would actually expect that to take the string value of the variable called value and use that as the key with which to set an attribute:

Which would be relevant to https://github.com/facebook/jsx/issues/108

endash commented 6 years ago

@briman I reconsidered the similarity about 10 seconds after I hit submit. I agree it doesn't jibe with es6 semantics. I don't think the superficial similarity is a plus or a minus in this case. Having brackets as an operator predates the new(ish) object literal syntax, and has other existing non-object-literal uses where it doesn't denote variable reference, notably in CSS selectors.