facebook / jsx

The JSX specification is a XML-like syntax extension to ECMAScript.

Home Page:http://facebook.github.io/jsx/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Proposal: ability to return multiple chlidren by auto-wraping them into a Fragment

mrnossiom opened this issue Β· comments

Hello there πŸ‘‹πŸ»,

I had an idea during the day and I wanted to submit it to confront others' though...
I searched for any duplicates but didn't find any, if I missed one please excuse me.

Describe the solution you'd like πŸ“–

The idea is to automatically warp all siblings tags in a JSX Fragment if there is more than one child.
This would give something like:

const Homepage = () => (
	<Head lorem={'ipsum'} foo={'bar'} />
	<main>
		<section>
			<h1>Headline</h1>
			<Headline />
		</section>
		<section>
			<h1>About</h1>
			<About />
		</section>
	</main>
);

when transpiled to ECMA has a wrapping JSX Fragment:

const Homepage = () =>
	React.createElement(
		React.Fragment,
		null,
		React.createElement(Head, { lorem: 'ipsum', foo: 'bar' }, null),
		React.createElement('main', null /* Children tags */)
	);

I did the example with the React API to illustrate but I know that it is a specification and it should not be used in the JSX spec doc.

Documentation, Adoption, Migration Strategy ⚠️

I can document myself and make a pull request, but I would like to know React developers' feedback.

Thanks for your attention and any answers.
MrNossiom

commented

I'm not a React developer but I'd still like to share my views on this. I really think this doesn't provide much value but has quite big semantic implications. It would imply that consecutive jsx element expressions will automatically will form a big fragment expression if and only if it's not contained withing a parent jsx element expression.
Let's look a the following example:

return <a />
	<b>
	</b>

Previously this would have returned <a/> and <b></b> would be it's own statemen and dead code. But your new approach would mean 2 (or more) adjacent expressions would form a new expression with new semantic implications. When thinking about it in terms of operators it's an adjaceny operator, which doesn't currently exist in any way in ecmascript if I'm not mistaken.
I may be mistaken but I think currently any 2 adjacent expressions in ecmascript will either be turned into 2 expression statements if the first was an expression statement or cause an error.

On greediness

This also opens some questions about greediness. Let's look at the next example:

a=<a/>
<b/>
<c/>

could be turned into the following

// Variant A
a=<a/>;
<b/>;
<c>;

Which in most cases would probably the intended semantics. But it could also mean

// Variant B
a=<><a/><b/><c/></>

As of now variant A is the result, it would also be what your proposal produces when implemented in a non greedy way. But this would also lead to the dead code seen in the first example.

Parentheses

One way around this ambiguity is what you demonstrated yourself. Wrap the expression in parentheses. Thus you enforce it all to be parsed as a single expression as it would otherwise create a syntax error.
This could be seen in one of 2 ways:

  1. Parenthesese have a special syntax case for adjacent jsx element expressions.
  • this would be a very weird special case and would feel very out of place
  • applying this to return statements as well would be even less fitting for ecmascript
  1. It applies in any expression where which contain adjacent jsx element expressions including a couple of interesting ones.
  • [<a/></b></c>] arrays
  • {a: <b/><c/>, d: <e/><f/>} objects
  • a(<b/><c/>) function calls
  • a+<b/><c/> infix operators (arithmetic, logic, and nullish-coalesce expressions)
  • template interpolation

Operator precedence

Next up: which precedence and other operator/relation properties would it have?

<a/><b/>||<c/><d/>
// equivalent to
(<><a/><b/></>) || (<><c/><d/></>)
// or
<><a/> (<b/>||<c/>) <d/></>
// or
<><a> (<b/> || (<c/><d/>)) </>

Conclusion

In order for the change to be non-breaking it needs to be non-greedy.
In order for there to not be some obscure special case in return statements or parenthesese it needs to be understood as an adjacency operator.
There is no such adjacency based syntax and expression operations in ecmascript at the moment so the introduction wouldn't fit into the lanuage.

As you see what you probably thought of as a small change actually has a lot of big implications. As I don't think adjacency fits ecmascript unless it's governed by a surrounding hierarchy level I would say this doesn't fit the JSX standard. It's implications are quite big even if the benefit is just saving 4 characters per function ( <> [...] </>->( [...] )) it's absolutely not worth it in my opinion even if implemented in a non-breaking manner.

Hello @QbDesu,

Well, first I didn't get that far in my thought, but I'm glad you did.
I made this PR to help solve an issue I had with Next.js.

When you create pages components, you often get a lot of tabulation:

const MyPageComponent = () => {
	<>
		<SEO title={'foo'} desc={'long bar'}
		<PageWrapper>
			<Title>My long title</Title>
			<main>
				<section>
					<Paragraph>And a nested paragraph</Paragraph>
				</section>
			</main>
			{'...'}
		</PageWrapper>
		<Footer>Here is some custom footer</Footer>
	</>
}

If you get a tabulation level off, it is already a lot.

But since I didn't get any answers from the React Team, I'll close this issue.