jsx icon indicating copy to clipboard operation
jsx copied to clipboard

RFC: JSX shorthand attributes

Open GnsP opened this issue 4 years ago • 8 comments

Support shorthand syntax for passing variables as props, where the variable name is same as the prop name; similar to the shorthand properties in ES6 object literals.

For example : <Component prop1={prop1} prop2={prop2} /> can be written as <Component +prop1 +prop2 /> using the shorthand syntax.

This RFC was originally put on reactjs/rfcs. Here is the relevant PR for that. The full text of the RFC is given below.

Summary

Provide a shorthand syntax for passing variables as props, where the variable name is same as the prop name; similar to the shorthand properties in ES6 object literals.

Basic example

function Toggle ({ onChange, checked, className }) {
    const toggleClasses = classd`checkbox-toggle ${checked && 'active'} ${className}`;

    function onClick () {
        onChange(!checked);
    }

    return <input type='checkbox' className={toggleClasses} +onClick +checked />;
}

This snippet transpiles to

function Toggle ({ onChange, checked, className }) {
    const toggleClasses = classd`checkbox-toggle ${checked && 'active'} ${className}`;

    function onClick () {
        onChange(!checked);
    }

    return <input 
        type='checkbox' 
        className={toggleClasses} 
        onClick={onClick} 
        checked={checked} />;
}

Motivation

While using react, it happens quite a number of times that we need to:

i. pass a prop down the component chain with the same name ii. pass a variable / function with the same name as the prop

In such cases, we need to write the same name twice as propName={propName}. This is redundant. Also with the increase in the use of function components and useState hooks, the usecase for such props is ever increasing.

Providing a shorthand syntax for passing such props would make the code concise and encourage developers to write cleaner and less redundant code.

Detailed design

The shorthand syntax, +propName (as recommended in this RFC) is to be supported by existing JSX transpilers. It can be done by updating the parse and transform functions for JSXAttribute nodes.

NOTE. This design is intended to work only with JSXIdentifier nodes prefixed with + in the JSXAttribute.name field. It should not work with JSXNamespacedName and should throw an Unexpected Syntax Error otherwise.

In parseJSXAttribute

  1. check is the Token starts with a +, i.e. charCodes.plusSign (ascii, 43)
  2. if true: i. eat the tokens.plusMin Token. ii. start a JSXAttribute Node node. iii. call parseJSXIdentifier and set node.name and node.value to the parsed JSXIdentifier. iv. finish and return node
  3. otherwise continue with the existing parseJSXAttribute logic.

In transformJSXAttribute visitor (entry phase)

  1. Check if the value in the current node (path.node.value) is a JSXIdentifier.
  2. If true, set the value in the current node (path.node.value) to a JSXExpressionContainer containing an Identifier with the name same as the name of the JSXIdentifier. i.e. types.jsxExpressionContainer(types.Identifier(path.node.value.name)) and return.
  3. Otherwise continue with the existing visitor logic.

Drawbacks

  • It goes against the principle "There should be one-- and preferably only one --obvious way of doing it". With the introduction of shorthand attributes, the way of writing JSX attributes becomes 4:
    1. The Standard <Component propName={expr} />
    2. Spread attributes <Component {...props } />
    3. Boolean attributes <Component booleanProp />
    4. Shorthand attributes <Component +prop1 +prop2 />
  • There may be confusion with boolean attributes, the syntactic difference being only the + prefix.
  • This proposes adding a new/different semantics to the existing unary + operator, even though this addional semantics is valid only within the context of JSXAttributes Nodes.

Alternatives

  • Maintaining the status quo, i.e. requiring the propName and the propValue to be written explicitly.
  • Using any other symbol than + as the prefix, so that we do not add to semantics of existing operators. maybe @.

Adoption strategy

This is not a breaking change and does not require any change to the codebase of React itself. It needs to be implemented by JSX transpilers and can be made available with their release cycle. Create-react-app, react-scripts, Linters and other existing tools need to support use of newer version of the JSX transpiler (plugin).

Existing react projects will still be valid with the newer syntax without requiring any change. But styleguides and linters can provide the options like --allow-jsx-shorthand-attributes to process the newer syntax as valid, and --use-jsx-shorthand-attributes to convert existing code to use the shorthand syntax where applicable.

How we teach this

If accepted, the shorthand syntax can be specified and taught in the React/JSX documentation. Conceptually using this shorthand syntax is optional, just like using <> </> for Fragments. The teaching strategy, therefore, can be similar to that of the Fragments shorthand.

GnsP avatar Sep 05 '19 18:09 GnsP

Using + seems confusing; as +identifier coerces to a number in JS contexts.

ljharb avatar Sep 06 '19 19:09 ljharb

@ljharb what if we use any other symbol than + as the prefix, so that we do not add to semantics of existing js operators ? maybe @ or & . & does not have any associated semantics as an unary operator / prefix.

GnsP avatar Sep 06 '19 19:09 GnsP

True, but <a b &c> does, because b &c is a bitwise operation in JS.

As for @, since that's effectively reserved for decorators, I'd suggest avoiding that as well.

The only syntax I'm aware of that doesn't have conflicting conceptual overload is <a b {c}>.

ljharb avatar Sep 06 '19 19:09 ljharb

True, but <a b &c> does, because b &c is a bitwise operation in JS.

As for @, since that's effectively reserved for decorators, I'd suggest avoiding that as well.

The only syntax I'm aware of that doesn't have conflicting conceptual overload is <a b {c}>.

@ljharb , yes, that's possibly valid for all symbols with binary operator semantics. <a b {c}> does not have conflicting conceptual overload, but would not it be unusual to allow expressions within braces in almost all contexts in JSX, except for attributes ? In case of shorthand attributes with the syntax <a {c}>, the only valid expression type for c is Identifier. But in case of <a b={c}>, c can be any valid expression.

GnsP avatar Sep 06 '19 19:09 GnsP

Though, in case of JSXSpreadAttributes, we are already limiting the possible types of expressions within the braces {}. I suppose that's quite an exceptional case.

GnsP avatar Sep 06 '19 19:09 GnsP

What about plain ES shorthand notation, which already works? 🙃

<input {...{ disabled, onClick }} />

asbjornh avatar Oct 24 '19 16:10 asbjornh

as a heavy react/jsx user, this feature looks awesome, to say the least. please, please approve it relevant: https://stackoverflow.com/questions/64700558/most-concise-way-to-pass-props-to-a-react-component

yigalirani avatar Nov 05 '20 18:11 yigalirani

What about plain ES shorthand notation, which already works? upside_down_face

<input {...{ disabled, onClick }} />

I prefer this a lot. It's more in line with existing JS syntax (because it just is JS syntax). But the syntax I'd suggest would just be the syntax suggested in #23

return <input {disabled} {onClick} type="button" />

This does more closely resemble JS syntax being reminiscent of shorthand properties on objects. In fact it could even be compiled to exactly that:

return React.createElement("input", { disabled, onClick, type: "button" });

QbDesu avatar Oct 16 '21 22:10 QbDesu