-
Notifications
You must be signed in to change notification settings - Fork 113
Discuss Conditional JSX Expression #35
Comments
Continuing the discussion fragment in #28 (comment): Yes, I was implying statement like syntax, but not just in a JSX expression. I was proposing that all the JSX expressions BE statement syntax. So the presence of a JSX expression is equivalent to With regards to use of non-standard javascript: Yes, the line is a little blurry. Especially as we add ES7 features before they're is agreement at the standards committees. But the difference is, imo, the answer to the question "What is the surface area of JSX?", and what is "other stuff". Unless we have some sort of statement syntax, the expression That's just my two cents. I encourage discussion on these points, and am always interested in exploring alternatives. |
I see two big issues, it messes with implicit indicies in a way that expressions doesn't (because all children, null or not, increment the index counter too):
It's also super unintuitive behavior that only made sense in PHP because it's concatenation of partial strings, not hierarchies:
Yeah ofc, I said that on the assumption that it will pass (no idea if it will), but array comprehensions probably aren't really relevant to this discussion. Anyway, my mine gripe is specifically with the ternary operator for conditionals, it seems like such a massive disservice to the readability of JSX and React, despite being such a common operation. Anything more complex than that is usually best hidden behind a function call anyway. On a hunch, I would be perfectly happy with just:
No nesting or anything else. Again, arguable whether one should do it or not, but it's very limited in scope and I find it far preferable to the ternary operator (which is just symbol soup):
|
I agree that JS's ternary isn't as ideal as but for the simple case you could just use JS's short circuiting logical operators, right? <div>
{x && ValueA}
</div> It would be nice for JS to have if-else expressions (instead of statements) a la CoffeeScript et al for the ternary case but JSX isn't the right place to address that IMHO. I've just been breaking those things out into methods/other components. |
That seems even worse to me, especially if you consider
I guess that's the point of this discussion, there is definitely a point where JSX could go too far, but I also think there's a point where JSX doesn't do enough. Ternary operators weren't designed for this and so just because they can doesn't mean they should be used. Conditionals is a very common use-case so neglecting it for philosophical reasons seems odd. Personally I also think there's a point in discussing how it should be, regardless of whether we do it or not (we can conclude what JS offers is a good enough approximation). To put it differently, you can say |
@syranide Would you be happy with:
It's a little more verbose, but the upside is that you would also preserve the hierarchical readability. Would that be sufficient? |
@jimfb how would you specify or / and condition with this? Though it's readable, it's on the other hand misleading as it takes the same syntax of components |
@lionng429 There are a couple of ways you could imagine specifying the else block. You could do The fact that it looks like a component is intentional. Conceptually, you've just imported the "If" component, which renders the children iff the test condition is true. It achieves the goal (more readable conditionals) without muddying the javascript syntax in a non-standard way. |
I'm not sure if I'm the ultimate authority on this, I'm simply a bit displeased with the current situation and hoping there is a better alternative :) Anyway, not a fan, repurposing elements seems like a bad idea, you would never consider My view on this subject is that I really respect the hands-off approach JSX has, it introduces elements, end of story. That's great. However most languages (including JS) were not designed with deeply nested expressions, like JSX, in-mind. The only conditional expression operator ( Intuitively it could make sense to have
This solves every problem (I think?) while not intruding on the host language. The only downside is the mandatory and predominant
To me that actually seems kind of neat and unintrusive, instead of having to move all complex code out into separate functions you can now do it inline and later refactor it out if necessary. It doesn't intrude on simple expressions and the ternary operator can still find itself useful for the simpler conditionals. One could also imagine replacing the
This syntax should be transpilable to statement syntax quite easily. But no doubt controversial, and the benefits are arguable when the I feel like tl;dr The example below seems like it could be a reasonable compromise to me:
|
It's not a philosophical reason. It's a huge tradeoff in complexity, both for tools and users. Right now it's just "stuff in brackets are JS expressions." Easy for for everybody to understand. And adding a special grammar for within brackets could wind up being an even bigger nightmare when new stuff gets added to the language.
So do I! I just don't agree that that's how it should be (: I don't mean to be dismissive or shrug my arms. When I say that I don't think JSX is the right place to address this, I don't mean that I don't think it should be addressed. But this is a problem in all of JS. Have you considered putting together a proposal for the language? |
Agree. I'm also not convinced the A favorite quote from Sebastian:
FWIW: The component/element syntax is available today: https://github.com/valtech-au/jsx-control-statements (personally I'm a fan). |
@matthewwithanm Yeah I'm not disagreeing that there are trade-offs for some approaches, but JSX has a purpose and if it's not 100% fit for that purpose we should evaluate if we can make it better.... that's why we have JSX to begin with, JSX is a monstrous trade-off for something which has barely quantifiable benefits, but we all agree it's worth it. So obviously it's not that binary.
I don't see how it's a problem with JS. Sure you could extend JS with it but I'm not sure what I would use it for other than possibly for JSX expressions. That last one wasn't really a serious proposal though.
@jimfb If you think of it in-terms of But the exact syntax isn't my point, but whether or not JSX has shortcomings. My experience is that JSX works surprisingly well as-is today when you have the time to carefully plan and organize your components. That is a luxury. Being able to move fast, experiment and later refactor is important. This seems like a small price to pay for that flexibility. |
FWIW, Babel actually implements do expressions. With this it means that the original example of:
can be represented as:
with a do expression. Babel implements the correct completion records semantics and outputs simple code and deopts to an IIFE for code it can't explode into a single expression. You can play around with it in the REPL here. IMO a more generic JavaScript solution (ie. do expressions) should be sought rather than adding additional complexity to the existing JSX syntax. |
Also, I really like @syranide's proposal of allowing an abitrary list of statements inside JSX expression containers, I particularly like the completion record example of:
It doesn't impose any new syntax and reuses existing JavaScript semantics. This could also quite trivially be implemented in Babel at least and the benefits would be enormous. |
@sebmck With regards to If I add As for @syranide 's proposal, 👍 as well. |
do expressions are exactly the kind of thing I was hoping for! Thanks for the link @sebmck! If the "statement problem" is solved in JS land like that, the only change in JSX semantics is automatic wrapping in do expressions and there's no chance of issues. Would love to see that happen. |
@mathieumg Yeah, eval("switch (1) { case '1': 2; break; }");
eval("switch (1) { case '1': 2; }"); |
@sebmarkbage Just curious if there is ANY documentation for do-expressions on the babel site? I couldn't find the proposal mentioned on the tc39 github thing so I did open this issue: tc39/ecma262#49 |
@matthewrobb There isn't. It's a deliberate choice because there's no current proposal document or spec text. |
JSX isn't the right context to discuss this really. The whole point of JSX, as opposed to templates, is to follow the general purpose language around it. If something like this needs to be added to JSX, it also needs to be added to the language, e.g. in an object literal or any other expression form, and we should follow that. We're not inventing a new language here so I kind of feel like this discussion is in the wrong place. It should be at es-discuss or something like it. Making do-expressions implicit (facebook/jsx#39) is a lot more palatable since the We can keep this issue open for discussions of the implications but I would suggest that we refrain from proposals that doesn't make sense in JS outside of JSX. IMO, if control flow syntax doesn't work in an object literal, it shouldn't work in JSX. I feel like facebook/jsx#39 + regular JS proposals like array comprehensions cover everything here. We can add a section to this repo with examples that demonstrate them. PR? |
I felt similarly when thinking about the "conditional challenge" in React. I've seen a lot of component based approaches, all of which are really well done, but I like the separation of control flow and my components. I put together a functional approach to this problem that I think contributes to this discussion. https://github.com/ajwhite/render-if I've shared this in another discussion over in facebook/react#690 (comment), so I'm just going to grab some samples I posted in verbatim: As an in-line expressionclass MyComponent extends Component {
render() {
return (
{renderIf(1 + 2 === 3)(
<span>The universe is working</span>
)}
);
}
} As a named functionclass MyComponent extends Component {
render() {
const ifTheUniverseIsWorking = renderIf(1 + 2 === 3);
return (
{ifTheUniverseIsWorking(
<span>The universe is still wroking</span>
)}
)
}
} As a composed functionconst ifEven = number => renderIf(number % 2 === 0);
const ifOdd = number => renderIf(number % 2 !== 0);
class MyComponent extends Component {
render() {
return (
{ifEven(this.props.count)(
<span>{this.props.count} is even</span>
)}
{ifOdd(this.props.count)(
<span>{this.props.count} is odd</span>
)}
);
}
} Or just vanillaconst ifEven = number => element => elseElement => {
if (number % 2 === 0) return element;
return elseElement;
}
class MyComponent extends Component {
render() {
return (
{ifEven(this.props.count)(
<span>{this.props.count} is even</span>
)(
<span>{this.props.count} is odd</span>
)}
);
}
} To me, this approach felt a more at-home with how React is structured by following the language around it. I find myself in agreement that control flow tags don't feel quite at home built into React as components. |
@ajwhite The only issue I see with those approaches is that the components will be created every time no matter what. You aren't leveraging any language construct to prevent needless construction of components or elements. |
@matthewrobb Yes, you are absolutely correct. @ajwhite This is a bigger problem than you might be imagining, and is the same reason that If statements can not be implemented as React components: https://github.com/AlexGilleran/jsx-control-statements/wiki/Why-Transform Alex has done a great job maintaining a transform that implements various control statements: |
Ah great point guys. Yep, this absolutely becomes evaluated. I suppose the only way to avoid evaluation here would be pass in as a callback {ifEvent(this.props.count)(() => (
<span>{this.props.count} is even</span>
)) However, this starts to lose the value I see in the goal of keeping this minimal. Thanks for this feedback, that can pose a big problem if attempting to access properties you only expect to exist when the predicate is satisfied. |
I think Microsoft got it right when making their Razor templating language. They tried to reduce the number of control characters {} and making the compiler just a little smarter. It's actually very similar to JSX in a lot of ways. @ puts you into code mode, until it hits an html literal or the end of the expression. Ends up much cleaner than handlebars.
|
I'm not a fan of the special control statements that I've seen suggested so far, so for only the simplest (IF) case I'd like to suggest annotation blocks on JSX expressions. (This also shares this new syntax with #66 Custom attribute namespace.) Then use the truthy value of the annotation block as the conditional and it makes simple boolean conditional rendering quite nice. (the object form below is the custom attribute namespace stuff)
Which could roughly look like this:
|
@chrisregnier I like the look of that. It does introduce a new control character, but doesn't clutter the code or feel awkward. I'm interested to see how What about
That's semantic, allows for single lines or blocks, can do |
@rozzzly I know my suggestion might not be the easiest to implement but it really does end up looking a lot cleaner and would be way easier for people to learn. (as I said microsoft have used this syntax perfectly fine in the past https://github.com/aspnet/Razor ) With the exception that it wouldn't support text literals directly in a code block (not dissimilar to jsx anyway), and probably require exactly one html element per code block. That said given that {} is already a control statement I can see why this could be quite problematic / ambiguous in certain situations.
One of the best parts of jsx is that it went down the route of smart parser and not custom templating language. Stick with that and use javascript syntax for control statements and html for html. |
I don't mind that, but I see it potentially making it a little more confusing what is expected ( |
@rozzzly other reason I like my suggestion (although yours would work with a small tweak) is that it opens up other code constructs too. The eg a for loop is suddenly very easy.
|
Along with @kittens & @syranide I would love an approach like
These code constructs work in vanilla javascript. For instance, type this in your console - it works:
Also, according to @kittens, this could be trivially implemented in Babel at least. I'm not sure how much more complicated it would make the JSX spec. Also, FWIW, I strongly dislike the idea of adding @calebmer @RReverser @sebmarkbage and other Facebook peeps have an opinion? And FWIW, I think this is an essential question to iron out for the future of React. I don't think |
But Would this work with your proposed syntax? <ul>
{for(var i in items) {
var t = fn(i); // in code mode until it sees a tag
<li />
}}
</ul> Also, I hate attribute directives like Angular has. Stringly-typed code in faux-attributes is the devil. |
From @syranide
The text was updated successfully, but these errors were encountered: