reactjs / rfcs

RFCs for changes to React
MIT License
5.51k stars 557 forks source link

[Feature request]: Add new syntax for jsx classes #155

Closed MaxmaxmaximusGitHub closed 4 years ago

MaxmaxmaximusGitHub commented 4 years ago

I remind you that JSX itself is a SUGAR on top of javascript ;)

💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡 💡 We need more sugar! New syntax for jsx classes! (and isolated styles) 💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡💡

Summary

new JSX class syntax: 1) attr start with dot is class name (intentionally similar to css, yes) 2) if this attr have expression value, then if exp is true, then class exists, otherwise not exist 3) of course, these classes have an ISOLATED SCOPE, for the ISOLATION of styles, I came up with this syntax.

Basic example

this:

<div .block .chat .active={ state.active }></div>

compile to:

<div className={ `block chat${ state.active ? " active" : "" }` } ></div>

by the way, no one forbids you to add dynamic class names, and still use your favorite css modules

this:

<div .[css.button] .[css.active]={ user.active } ></div>

compile to:

<div className={ `${css.button}${ user.active ? " " + css.active : "" }` }></div>

Motivation

Attributes names with started a dot symbol is prohibited, so dot is idle idle. dot can be used for syntactic sugar for routine 90% of the operations of assigning classes to elements.

people will be able to write a main class describing what kind of entity it is. and add dynamic modifier classes, which reflect the component state.

it’s just syntactic sugar. but it will save 90% of the time for developers. the programmer simply type on a keyboard the dot symbol, and the IDE will automatically autocomplete them for the available classes.

in brevity and elegance, this syntax is even superior to html ;)

our beloved jsx is high time to develop, because competitors (angular and vue) do not stand still ;)

Detailed design

you just look how cool:

<ul .users>
  {this.props.users.map( user =>
      <li .user .banned={ user.isBanned } .admin={ user.isAdmin }>
        User { user.name }
      </li>
  )}
</ul>

and imagine how it will be cool with syntax highlighting in the IDE and if you click by mouse wheel you will immediately be transferred to the desired selector in css file.

what it looks like now:

<ul className="users">
  {this.props.users.map( user =>
      <li className={ `user${ user.isBanned ? " banned" : "" }${ user.isAdmin ? " admin" : "" }` }>
        User { user.name }
      </li>
  )}
</ul>

feel the difference?

this:

<div .box .active={active} onClick={toggleActive}></div>

compile to this:

<div className={ `box${ active ? " active" : "" }` } onClick={toggleActive}></div>

just compare

export default function TabTitle({active, onClick, children}) {

  return <div className={ classes('tab-title', {
    __active: active
  }) } onClick={ onClick }>

    { children }

    <style jsx>{ style }</style>
  </div>
}

and my version:

export default function TabTitle({active, onClick, children}) {

  return <div .tab-title .__active={active} onClick={ onClick }>

    { children }

    <style jsx>{ style }</style>
  </div>
}

is a syntax hell :

image

We can use new syntax and old syntax at the same time:

this:

function classes(classesArr){ classesArr.join(' ') }

<div .box .active={active} className={ cssModule.class + classes(['one', 'two']) }>

compile to:

function classes(classesArr){ classesArr.join(' ') }

return <div className={[cssModule.class + classes(['one', 'two']), `box${active ? ' active' : '' }`].join(' ') }>

Drawbacks

Why should we not do this? Please consider:

There are no drawbacks, since the dot symbol was previously prohibited, backward compatibility is present.

The disadvantage is that jsx with the new syntax will cause a syntax error in compilers that do not support the new function. therefore, we must write babel-plugin to translate the new code into the "old" jsx.

Alternatives

You can write the babel plugin to support the new syntax, but this will not motivate the creators of the IDE to add new syntax. The popularity of React and Facebook and the headline "React 17.0 - New Syntax for Classes." will motivate support this feature.

Adoption strategy

React competitors use much more convenient syntax than jsx. Since it uses attributes starting with special characters. jsx does not use such attributes, and simply prohibits them. People are tormented by class definitions in jsx and rejoice in Angular and Vue. People will be happy with the new “meaningful” syntax, which will be much more organic than native html. Since in css classes start with a dot, in jsx they will also start with a dot. And also the most common operation with classes is adding or removing a class, depending on the state. We add and make it native. There is not a single developer in the world who says "bliiin, well, support for convenient classes has appeared (" and there are 99% of people who say "FINALLY" =)

How we teach this

This innovation is sugar, and does not break or change existing behavior. Therefore, we can simply ADD a new section to the "dot classes" documentation. Those who like the new syntax will use it, those who like the old will use the old. Anyone who likes both syntaxes can combine them. className is just a string, so at the jsx compilation stage we can quietly unify both methods of class declaration.

Unresolved questions

Since we are doing such a thing, then maybe we should immediately think about isolated styles for the component? Since we are making syntax changes, it may be that we think over the isolation of styles in advance? After all, if we implement this without thinking it over, then we will not be able to change it on the future. I suggest thinking about how to natively (from the point of view of jsx) and syntactically solve the problem of encapsulating classes, and how to make it flexible enough so that other encapsulation libraries can USE this syntax.

by the way, we can also add dynamic attribute names

this:

let prop = "ololo"
<div [prop]="value"></div>

compile to:

let prop = "ololo"
React.createElement('div', { [prop]: "value"} )

plus this syntax allows you to finally do this:

function List(props) {
  return <ul .list {...props}></ul>
}

function Component() {
  return <List .red></List>
}

its render to:

<ul className="lisit red></ul>

ability at the precompilation stage to competently concatenate dot classes and className is phenomenal =)

Adequate guys, do you want me to write a transpiler plugin for this syntax, and add support for it in your IDEs? Who agrees? I think to create a separate repository =)

UPD:

cheers guys, I came up with a workaround !!!! thank god these guys did not forbid the dollar symbol !!! =)

<div $chat $active={ false } $opened={ null }></div>

I will write a babel plugin that implements the behavior that I described here. the dollar sign will temporarily act as a dot symbol =) this way we can test new syntax

yes, it breaks backward compatibility, since now you can’t use properties starting with the dollar sign, but it's not my fault, but the guys from React =)

but living even that way will be much easier. of course it's a fierce crutch, but at least something

p.s. yes and goodbye typescript

I experimented a lot, and technically, the length of such a syntax is literally equal to the length of the syntax I proposed:

image

but visually the dollar symbool is annoying and far less logical than the dot symbool.

to implement override and disable classes semantics, I will write the runtime mergeProps function, but this will be the next step

foray1010 commented 4 years ago

I downvoted this because:

  1. It doesn't stick to HTML syntax, it is hard to understand for new comer
  2. It is impossible to type it in flow or typescript as it is a dynamic prop name
MaxmaxmaximusGitHub commented 4 years ago

@foray1010

It doesn't stick to HTML syntax, it is hard to understand for new comer 1) it follows the html syntax, since ANY characters in attribute names are allowed in html. 2) jsx also does not adhere to the html syntax since in html you need to write a class and not className 3) jsx itself does not adhere to the html syntax since in html the value of attributs should be framed in quotes, and jsx allows the expression attr = {2 + 3} 4) The new syntax will not cause confusion among the beginner, but will increase the understanding of beginners, since it is superior to html and has syntactic integrity with css. 5) we don’t remove the old className syntax, we just add standardized sugar on top of it, which will improve static analysis, code autocompletion, and the way to define classes. =) 6) in general, we can add dynamic classes, but this contradicts the ideas embodied in this proposal

<div .user .user_profile_{this.state.user.role}_role ></div>

although it looks quite comfortable ;)

It is impossible to type it in flow or typescript as it is a dynamic prop name

purpose, sentences, to make class names static, not dynamically, therefore, you are completely diametrically misunderstood this sentence =)

foray1010 commented 4 years ago

Did u use typed language before? we need to predefined attribute types before use

mAAdhaTTah commented 4 years ago

@foray1010 This new JSX syntax would transpile down to className, so the TS/Flow issue wouldn't be an issue.

That said, JSX isn't DOM-specfic, so I'm not sure how acceptable new syntax that targets className specifically would be here.

j-f1 commented 4 years ago

I dislike this because it favors one means of styling (CSS using global class names) over all others (CSS modules, inline styles, CSS-in-JS, React Native, etc.) for no clear reason, and blocks JSX from ever using this syntax for something else in the future.

MaxmaxmaximusGitHub commented 4 years ago

@j-f1 no, this syntax is CREATED for isolated and SCOPED class names, you completely did not understand the idea. I, using this syntax in my framework, work with a bang https://www.npmjs.com/package/ui-js

MaxmaxmaximusGitHub commented 4 years ago

@foray1010

Did u use typed language before? we need to predefined attribute types before use

dot classes are not html attribute names. you just did not understand the idea. dot classes these are the lines that fall into the className property, when compiling jsx

MaxmaxmaximusGitHub commented 4 years ago

@mAAdhaTTah

That said, JSX isn't DOM-specfic, so I'm not sure how acceptable new syntax that targets className specifically would be here.

now attributes starting with a period are generally prohibited. no platform uses them. There are no conflicts with other platforms, just a bonus for the web platform. benefit is - there is no harm.

moreover, one can simply introduce the agnostic concept of a component class. and React for web will implement it as a css class. and React Native will implement it as something else for example.

image

mAAdhaTTah commented 4 years ago

Unless the feature is broadly useful, I don't see this being accepted as part of JSX. You implicitly recognize there's no use for it in React Native, and there is also no clear use for it in other rendering targets like canvas, webgl, & the terminal, all of which have React renderers. New syntax that's only useful in one case doesn't really clear the bar for adding it.

dantman commented 4 years ago

@j-f1 no, this syntax is CREATED for isolated and local class names, you completely did not understand the idea. I, using this syntax in my framework, work with a bang https://www.npmjs.com/package/ui-js

Your first post gives this example:

<div .block .chat .active={ this.state.active }></div>

its equivalent for

<div className={ `block chat${ this.state.active ? " active" : "" }` } ></div>

How are these global class names "isolated and local class names"?

MaxmaxmaximusGitHub commented 4 years ago

@mAAdhaTTah

Unless the feature is broadly useful

it is very useful, since now it is inconvenient to make dynamic classes based on variables. and this function will make it convenient. this function will save 40% of the time during layout. Is saving 40% of the time in layout not a useful feature?

@dantman

How are these global class names "isolated and local class names"?

These are not global class names, these are local class names. the programmer should not be involved in the manual addition of namespace, so you mistakenly assumed that these are global class names.

Next, I wrote:

1) Now that we have a static definition of class names, we can ANALYZE them at the stage of compiling the code, and we can add namespaces. In other words, the syntax proposed by me makes the work of transpilers that add namespaces and isolation systems to classes easier. Since I came up with a STANDARD for class descriptions.

Then I wrote:

1) Since we made the class description declarative and created a special syntax for it, we can also come up with a class isolation system and build it into the react, or leave this to the Babel type transporters.

You just did not understand my PHENOMINAL idea, like everyone who put dislike. Their programming level is fundamentally low compared to mine. Therefore, they are not able to realize such a revolutionary breakthrough and useful approach. They are still young, learn later.

speaking at the level of ordinary people, and not at my transcendental level of genius:

1) all current style isolation tools will work as before

2) new and tools for isolating styles can add insulation more conveniently

Example:

1) we write loader on webpack which scans jsx file

2) we import style.css file into it

3) in jsx we write

style.css:

* { font-size: 50px }
.box { color: red }
.box.active { color: green }

component.jsx:

import style from 'style.css'
import {useState} from 'react'

export default function Component() {

  const [active, setActive] = useState(false)
  function toggleActive(){ setActive(active => !active) }

  return <div .box .active={active} onClick={toggleActive}>

    // apply ISOLATED style
    // this tag WILL REMOVED by BABEL !!!! OF COURSE!! 
    // this is just a mark for Babel, where apply the imported style and ISOLATE it
    <style jsx> { style } </style>

  </div>
}

4) babel sees the