jsx icon indicating copy to clipboard operation
jsx copied to clipboard

Proposal: destructuring

Open lencioni opened this issue 7 years ago • 5 comments

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).

lencioni avatar Mar 24 '17 12:03 lencioni

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

jeffmo avatar Mar 24 '17 12:03 jeffmo

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 avatar Mar 24 '17 14:03 lencioni

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 }));

lencioni avatar Mar 24 '17 14:03 lencioni

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

beefancohen avatar Mar 24 '17 17:03 beefancohen

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.

sebmarkbage avatar Mar 28 '17 21:03 sebmarkbage