facebook / jsx

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

Proposal: destructuring #76

Open lencioni opened 7 years ago

lencioni commented 7 years ago

Let's say you have a function that returns an object with a known shape that you want to use as props. Currently with JSX, you would use spread:

<Foo {...bar()} />

This is compiled to the following:

React.createElement(Foo, bar());

Great! Now, let's say you want to add some other props to this.

<Foo {...bar()} baz />

When compiling for React, this is compiled to the following:

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

React.createElement(Foo, _extends({}, bar(), { baz: true }));

Which makes sense, but the Object.assign and _extends helper code here is a bit of a bummer if you happen to do this all over the place. Granted, there are ways to minimize the helper code, but you still have the overhead of Object.assign.

It might be nice to have a way to destructure in JSX to avoid this overhead. Here's an idea:

<Foo {fizz, buzz}={bar()} baz />

could compile to something like this:

React.createElement(Foo, (_bar = bar(), { fizz: _bar.fizz, buzz: _bar.buzz, baz: true }));

JSPerf: https://jsperf.com/object-assign-vs-shenanigans/1

This might dovetail well with AssignmentExpression in JSXAttributeName (#21) which is being considered for JSX 2.0 (#65).

jeffmo commented 7 years ago

I like that this helps to be precise about which props you want to spread (when not all of them). I suspect Flow (by way of users of Flow) might benefit from this precision... cc @samwgoldman

lencioni commented 7 years ago

And of course array destructuring could be done similarly:

<Foo [, second, third]={bar()} baz />

becomes

React.createElement(Foo, (_bar = bar(), { second: _bar[1], third: _bar[2], baz: true }));
lencioni commented 7 years ago

And assigning to new variable names:

<Foo {fizz: fizzy, buzz: buzzy}={bar()} baz />

becomes

React.createElement(Foo, (_bar = bar(), { fizzy: _bar.fizz, buzzy: _bar.buzz, baz: true }));
beefancohen commented 7 years ago

I think that maybe it could support default values as well.

<Foo {fizz = 1, buzz = 2}={bar()} baz />

becomes

React.createElement(Foo, (_bar, = bar(), { fizz: _bar.fizz === undefined ? 1 : _bar.fizz, buzz: _bar.buzz === undefined ? 2 : _bar.buzz, baz: true }

Although defaultProps can (and should) be used, at least this gives feature parity with normal destructuring

sebmarkbage commented 7 years ago

I think that we'll probably want to align with a proposal to general JS to do this in object literals rather than inventing our own way of doing this that then may get misaligned with a JS solution. Here is a plausible proposal that might satisfy this general use case for object literals:

https://github.com/RReverser/es-borrowed-props

We could do the same for JSX if this proposal advanced in JS.