jsx icon indicating copy to clipboard operation
jsx copied to clipboard

JSX 2.0

Open sebmarkbage opened this issue 7 years ago • 136 comments

We have accumulated a number of these nice-to-haves breaking changes that would be nice to incorporate. However, at this point JSX is a hugely adopted syntax in all kinds of tooling. Making breaking changes will churn that ecosystem significantly. I think it is probably only worth doing as a batch when the accumulated amount of changes makes it worth while.

Let's call it JSX 2.0.

What should go into it?

Since a completely incompatible version of JSX has landed in Reason it would be good to find a way to unify them.

I'd say at least we'd want:

  • #4 - Drop HTML encoding in text and attributes.
  • #21 - Computed attribute names.
  • #23 - Object short hand notation.
  • #25, #51, #64 - Drop the need for curlies around attribute values if they're a single literal, or parenthesis.

Some more controversial/problematic ones that could plausibly be fixed:

  • #39 - Implicit do expressions.
  • #35 - Drop implicit text content and curlies as children.
  • #66 - Custom attribute namespaces.

What else?

cc @jordwalke @SanderSpies

sebmarkbage avatar Oct 12 '16 20:10 sebmarkbage

I would be interested in having a real story for comments e.g. #7. {/**/} feels like a hack, and kind of also is as it affects the children when used in certain places. If we actually end up going with #35 (bold, I like it :)) then just properly supporting regular comment syntax is a no-brainer IMHO.

syranide avatar Oct 13 '16 07:10 syranide

Also, having an actual syntax for fragments would be a very welcome feature (e.g. the previously talked about <>).

syranide avatar Oct 13 '16 08:10 syranide

what would it be for fragments?

const frag = <>
    <div>hello</div>
    <div>world</div>
</>;
// vs 
const frag = [
    <div>hello</div>,
    <div>world</div>
];

then it's the same, you'll need {frag} to insert it. I think arrays are good enough, similar to DOM fragments, and more powerful to manipulate

comments is a good idea, <!-- --> like html could be fine, but would they be inserted in the DOM (I guess no)? would it conflict with how react uses comments sometimes for text nodes I think

caub avatar Oct 16 '16 14:10 caub

but would they be inserted in the DOM?

There should be a setting to turn it on for dev / off for production in the implementing libraries.

theduke avatar Oct 16 '16 15:10 theduke

We need more restrictions on how JSX can be written. People are using terrible practices and teaching it to others. Example: child props should only be passed into components via childProps, otherwise it's a mess. There are so many ways to do things but few even ask if they should do it that way.

sbussard avatar Oct 16 '16 15:10 sbussard

Something like Angular ng-if would be nice to have in JSX. Making rendering of an element conditional is not easy in JSX. Maybe adopt Angular 2 *if

mohsen1 avatar Oct 16 '16 15:10 mohsen1

Most of these changes wouldn't affect my codebase since I use them sparingly. Comments however would be great. It's kind of insane that normal javascript commenting isn't supported even with all the crazy tool chains we are running.

tachang avatar Oct 16 '16 15:10 tachang

I'd say allow adjacent elements without enclosing tag, really hate additional <div>s like this:

<div>
   <ComponentA />
   <ComponentB />
</div>

jasonslyvia avatar Oct 16 '16 15:10 jasonslyvia

@jasonslyvia That's what fragments are for.

syranide avatar Oct 16 '16 15:10 syranide

Would be nice to have something like how Adobe/Apache Flex deals with conditional inclusion of components. It is refered to as state in Flex world. You can assign a child component a state name and then when you set the parent components state it hides/shows child components as appropriate

alistairhutton avatar Oct 16 '16 15:10 alistairhutton

Hi guys! What do you think about this idea? https://github.com/alexander-shvets/cascading-component-layers-react

oleksandr-shvets avatar Oct 16 '16 15:10 oleksandr-shvets

@syranide It would be better if it could be solved in syntax level.

jasonslyvia avatar Oct 16 '16 15:10 jasonslyvia

<if {myCondition}>
  <div>This part only gets shown if myCondition is true</div>
</if>

lacker avatar Oct 16 '16 16:10 lacker

@mohsen1 our convention for our stateless functional components is to use:

import MyInnerComponent from '../my-inner-component';
function MyOuterComponent({ isInnerComponentIncludedBool }) {
  ...
  return (
    <div>
      <SomeOtherComponent />
      {
        isInnerComponentIncludedBool
        ? <MyInnerComponent />
        : null /*Or some fallback component / messaging*/
      }
    </div>
  );
}

which works pretty well for us. It's pretty clean as long as your outer component props are a clear pre-calculated boolean for show/hide. Because your components don't have any business logic, this should be fine already, right? ;)

edit: the ternary allows us to include an alternative, which we commonly do want, without needing to add an if !someBoolFlag check also.

BrendanFDMoore avatar Oct 16 '16 16:10 BrendanFDMoore

@mohsen1 @lacker @BrendanFDMoore

You can make it as easy as angular if like this, so I don't see any need for an if tag

{ somethingTrue &&
  <div>Will only show if somethingTrue is true</div>
}

kevinsimper avatar Oct 16 '16 16:10 kevinsimper

@BrendanFDMoore I'm aware of those workarounds and we're using them. Since JSX is syntax sugar for React.createElement adding more syntax sugar won't hurt.

In your example the syntax can look like this:

return (
    <div>
      <SomeOtherComponent />
      <MyInnerComponent *if={isInnerComponentIncludedBool} />
    </div>
  );

I guess mentioning Angular earns you lots of downvotes here!

mohsen1 avatar Oct 16 '16 16:10 mohsen1

@kevinsimper True, it's more compact. We stick to the other convention because instead of null we often want to show some placeholder text or replacement component.

@mohsen1 you're right, there's no harm in more helpful sugar to clean up unpleasant syntax where there is wide interest and a commonly accepted pattern. I'm not necessarily against the addition of an if helper.

BrendanFDMoore avatar Oct 16 '16 16:10 BrendanFDMoore

I find that assigning optional components to variables helps for clarity, for example:

function Profile({ user }) {
  let avatar;
  if (user) {
    avatar = <Avatar user={user} />;
  }
  return (
    <div>
      ...whatever the outer component represents...
      {avatar}
    </div>
  );
}

While it would be nice to have direct support in JSX for conditional components, I'm really wary of adding things like <if> or an if=... attribute. Next people will want <for> and <while>... IMHO there's no need to replicate that much JS functionality in JSX.

nkohari avatar Oct 16 '16 16:10 nkohari

Automatically bound arrow function that don't allocate a new function everytime:

<div onLoad={@this () => doSomething(blabla)}/>

fab1an avatar Oct 16 '16 16:10 fab1an

IMHO jsx has only several problems:

  • code comments
  • conditional attributes

JS should have separate context in curly braces.

max-mykhailenko avatar Oct 16 '16 16:10 max-mykhailenko

With current syntax {something && <Component />} when you render a more complex component this introduces another level of indentation. Which in my opinion helps us to spot conditional logic, but does not look as nice.

I think that having multiple ways of doing the same thing adds to overhead when reading the code. It's small but noticeable.

meznaric avatar Oct 16 '16 16:10 meznaric

Make whitespace handling more consistent with HTML: https://github.com/facebook/react/issues/4134 (sorry for brevity, currently mobile...).

jmar777 avatar Oct 16 '16 17:10 jmar777

@mohsen1 "It's just JavaScript, not a template language" -> no need to replicate JS functionalities with custom syntax. That is the main benefit of JSX IMO, seriously is so easy to do this with js even if it looks "weird" (for me it is not weird, it is just the syntax of the language)

bjrmatos avatar Oct 16 '16 17:10 bjrmatos

I would like to be able to specify some property value with tag :

<SplitContainer
  left={<div>...</div>}
  right={<div>...</div>}
/>
// vs
<SplitContainer>
  <.left>
    <div>...</div>
  </.left>
  <.right>
    <div>...</div>
  </.right>
</SplitContainer>

fdecampredon avatar Oct 16 '16 17:10 fdecampredon

@mohsen1 It has nothing to do with mentioning Angular but with introducing additional syntax that makes it harder to learn and harder to read without adding any benefit as it's more characters to type than {condition && VDOM} and it's harder to understand (&& will return the lhs if it's falsey as all JS, what will *if do? return null? undefined? rip out the element completely as it was never there?)

Also with #35 and #39 you could do

if (condition) {
  VDOM
}

wich would be JS instead of some special thing

Pajn avatar Oct 16 '16 17:10 Pajn

@Pajn That looks much better than *if or anything like that. Also #35 is a great proposal! 👍

mohsen1 avatar Oct 16 '16 17:10 mohsen1

Don't fuck it up like Google did with Angular 2, keep the thing compatible with older versions...

nkkollaw avatar Oct 16 '16 17:10 nkkollaw

I highly doubt that it makes sense to have full backwards compatibility for JSX 2.0. If we are going to improve it -- we should improve.

After all, no one is forced to migrate to JSX 2.0 and you still will be able to write your React apps in JSX 1. React elements/components are just JavaScript calls, so there is no matter what you are using. In some case, if you would like, you probably will be able to use JSX 1 and JSX 2.0 in the same project, e.g. for migration. Just apply new transform for new components.

IMO, that's it.

NekR avatar Oct 16 '16 17:10 NekR

Though, I'm not a fun of adopting features from Angular. Like some already here proposed are already possible with JSX + React.

NekR avatar Oct 16 '16 17:10 NekR

I would like more granular control over what function the JSX is compiled to.

/** @jsx myImageFunc img*/

import React from "react"
import myImageFunc from "./somewhere"

SomeClass extends React.Component {
     render() {
        return (
            <div> // compiled to default, ie. React.createElement
                <img src={...}/> // compiled to myImageFunc
            </div>
        )
    }
}

brochington avatar Oct 16 '16 17:10 brochington