Skip to content
This repository has been archived by the owner on Sep 21, 2022. It is now read-only.

Declarative jsx example #28

Closed
wants to merge 1 commit into from
Closed

Declarative jsx example #28

wants to merge 1 commit into from

Conversation

jimfb
Copy link
Member

@jimfb jimfb commented Mar 4, 2015

Potential direction for JSX. By late-binding variables in jsx expressions, we gain a lot of flexibility and allow for a much more readable/declarative syntax.

EDIT: Some people seem to be under the mistaken impression that I'm advocating the inclusion of tempting tags (like push, iff, and foreach) into the JSX langauge. That is NOT the case. The point of this example is to make it POSSIBLE to implement components like foreach (and incidentally justify the use case by showing that such a tag might be desirable for making your code more declarative and therefore more maintainable). The basic tenants of JSX (including the ability to easily jump into javascript at any time) would still exist with this change. The only difference is a slight addition to the variable scoping rule.

@jimfb jimfb force-pushed the jsx2 branch 9 times, most recently from be208f2 to c1abe1f Compare March 4, 2015 19:05
@jimfb
Copy link
Member Author

jimfb commented Mar 4, 2015

cc @sebmarkbage

@iamdustan
Copy link

I’m still unsure if I’m in the knee-jerk reaction phase to this or not, but I do not like this direction. This takes a step away from the“it’s just javascript”approach. One of the beautiful parts of React over alternatives like handlebars and angular is that there is no special syntax to learn or new concepts to understand.

While the example tag implementations are trivial, the cognitive load they create is much larger. A components implementation is no longer simple javascript if statements and maps, but a new, proprietary way to define these primitives with new scoping rules and syntax.

@gaearon
Copy link
Member

gaearon commented Mar 5, 2015

+1 to @iamdustan's comment

@jimfb
Copy link
Member Author

jimfb commented Mar 5, 2015

@iamdustan Everything is still just javascript. You're still able/free to use if statements and maps. In fact, notice that I did use map in my foreach implementation. I'm not advocating for any particular tags (if any) to be added to react core (though, push iff and foreach might be a good ones). The only change to React would be to allow tags to set variables, which ALLOWS for higher-level control tags like the ones in this example. It's still pure javascript, just making JSX a little more flexible in the type of tags it can describe. You can use/write your own custom control tags if you so desire, or not. It's no different from your decision to write a custom composite component, or not.

<foreach element="author" index="authorIndex" collection={book.authors} />
<a
href={"http://www.authors.com?id="+author.id}
style={{color: 'blue', textDecoration: 'none'}}>{author.name}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm interested in how that will be compiled to JS, as a function of author argument? How JSX compiler should decide when to compile JSX element as function and when as React element?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple of options for compiling JSX to JS, none of them particularly fantastic, but all workable. One would be to compile author.id to $scope.author.id. Another would be to use the with operator.

It's worth noting that the expressions will probably need to be wrapped in a function/closure and executed during render instead of during element creation, but that's not a big deal, should be virtually invisible, though there is a slight performance overhead of creating/invoking the anonymous function. This can probably be minimized by checking which variables are in scope and only converting to a function if you are using variables inherited from a tag.

Regardless of the exact desugaring syntax, it's the concept of allowing tags to bind variables that is most important/interesting.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reimplementation of scoping rules is why @iamdustan is saying it's not still "just JavaScript." Of course, anything you write will be "pure javascript" in the sense that it has to compile to JavaScript in the end—but it'll differ in terms of syntax and behavior, which is what adds the cognitive load.

I think the fundamental disconnect here is that your value proposition is that this would make it possible to approximate JS control structures in JSX, but much (most?) of the community doesn't see those control structure elements as something positive. Nobody is under the impression that you're advocating for the inclusion of tempting tags, but if the purpose of your proposal is to enable something they see as harmful, they're going to be against it on the grounds of enabling developers to "fall into the pit of success."

@Kureev
Copy link

Kureev commented Mar 5, 2015

@iamdustan +1. It looks like angular with JSX notation

@jimfb
Copy link
Member Author

jimfb commented Mar 5, 2015

@Kureev Haha, yeah, @sebmarkbage also smiled and said "looks like angular" when he first saw this. I hadn't actually looked at the angular syntax when I wrote this up (but I was inspired by Struts2, so I can't claim credit for coming up with it by myself). The fact that angular (and other frameworks) are using this pattern is an indication that it's potentially useful. We shouldn't reject it out of hand just because other people do something that looks similar :). We should decide if it's a potentially useful concept and then iterate.

@Kureev
Copy link

Kureev commented Mar 5, 2015

@JSFB Why you don't like the way it works now? Compute your DOM tree first (if it's needed) and compose it in the render function? It makes code more clean and neat. I can check the render function and tell you what exactly component is going to render. With your suggested approach it becomes more difficult (in my op). Probably, I just don't see benefits from iff or foreach in the syntax.

@SanderSpies
Copy link

Perhaps a different idea is better here: allow JS between tags and make
text the exception instead.
Op 5 mrt. 2015 12:12 schreef "Alexey" [email protected]:

@iamdustan https://github.com/iamdustan +1. It looks like angular with
JSX notation


Reply to this email directly or view it on GitHub
#28 (comment).

@jimfb
Copy link
Member Author

jimfb commented Mar 5, 2015

@Kureev This doesn't change that behavior. This code still goes into your render function. The only difference is that you are now able to implement tags like foreach, whereas previously you could not. The use of tags can make the code more declarative (ie. you don't have the whole mutate-a-list-and-insert-it-later problem demonstrated in the example) which makes your code easier to maintain (especially as component complexity increases).

@insin
Copy link

insin commented Mar 5, 2015

You could make the regular JavaScript version more declarative as a fairer comparison:

return <div>
  {books.map(book => <div key={book.id}>
    <h1>{book.title}</h1>
    By:{' '}
    {book.authors.length === 0 && <span style={{color: 'gray'}}>Unknown author</span>}
    {book.authors.length > 0 && book.authors.map((author, idx) => <span key={author.id}>
      <a href={"http://www.authors.com?id="+author.id}
         style={{color: 'blue', textDecoration: 'none'}}>
        {author.name}
      </a>
      {idx !== book.authors.length && ', '}
    </span>)}
  </div>)}
</div>

I really don't like the idea of making JSX be anything but sugar for React.createElement calls and composing props to be passed to them (and eventually sugar for tag literal objects). Once that's understood, you don't need a second thought about what's happening behind the scenes because there is no behind the scenes, just JavaScript.

(Aside: You could use a utility function to avoid the manual <span> wrapping above, which could also make it trivial to separate books with an <hr>)

return <div>
  {joinElements(books.map(book => <div key={book.id}>
    <h1>{book.title}</h1>
    By:{' '}
    {book.authors.length === 0 && <span style={{color: 'gray'}}>Unknown author</span>}
    {book.authors.length > 0 && joinElements(book.authors.map(author =>
      <a key={author.id}
         href={"http://www.authors.com?id="+author.id}
         style={{color: 'blue', textDecoration: 'none'}}>
        {author.name}
      </a>
    ), ', ')}
  </div>), () => <hr/>)}
</div>

@Kureev
Copy link

Kureev commented Mar 5, 2015

Totally agree with @insin

@insin
Copy link

insin commented Mar 5, 2015

Related: JSX Control Statements: https://github.com/valtech-au/jsx-control-statements

@syranide
Copy link

@jimfb (Sorry, just hijacking this briefly) http://wiki.ecmascript.org/doku.php?id=harmony:array_comprehensions could this be something to consider for JSX? I.e. statement like syntax inside braces, more readable than ternary and various other hacks while still being very much like JS. <div>{if (x) <div />}</div>, etc.

@jimfb
Copy link
Member Author

jimfb commented Jun 11, 2015

@syranide We don't want the syntax inside the squiggly brackets to be any non-standard javascript, so with jsx expressions as they are today, that syntax would be unlikely to fly. That said, if jsx expressions like <div /> translated into something like React.appendElement(...) whereby the act of 'invoking' the jsx expression resulted in it being added to the output of render, such a thing would be totally possible. My guess is that it deviates too far from JSX as we know it today, so this is unlikely to happen, but it is an alternative that I've found interesting to ponder. It is much closer to the way other languages (like php) operate.

@syranide
Copy link

We don't want the syntax inside the squiggly brackets to be any non-standard javascript, so with jsx expressions as they are today, that syntax would be unlikely to fly.

Yeah ofc, but if array comprehensions make it into the standard, that line becomes rather blurred to me. If the standard considers the special syntax important enough for array comprehensions I feel that the same could reasonably apply to JSX (ternary operators are not very readable). Just bringing up because I found it interesting, not going to fight over it ;), but it doesn't seem as clear cut "non-standard javascript" in this case to me.

That said, if jsx expressions like <div /> translated into something like React.appendElement(...) whereby the act of 'invoking' the jsx expression resulted in it being added to the output of render

Considering the conditional elements usually goes deep inside the hierarchy I'm not sure how you would do this (unless you're proposing statement like syntax inside JSX?). Also, this would (unintuitively) mess up implicit indicies if used in conditionals so it would probably not be a good idea.

@jimfb
Copy link
Member Author

jimfb commented Jun 11, 2015

@syranide Let's move this to #35

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants