Hexbyte  Hacker News  Computers React Fire: Modernizing React DOM · Issue #13525 · facebook/react

Hexbyte Hacker News Computers React Fire: Modernizing React DOM · Issue #13525 · facebook/react

Hexbyte Hacker News Computers

Regarding class and className.

I know we won’t get a broad agreement on this whichever way we go. People have really strong opinions about this one. I want to share how I’m thinking about it, in a hope that it will be helpful.

Component API Should Feel Idiomatic

There is a common argument that React “matches JavaScript” and thus className is preferred. I think this assertion is subtly misunderstood so I’d like to focus on it a little.

In React, first and foremost, we care that using a React component should feel like idiomatic JavaScript. This means that if I use a hypothetical

component, I expect its props to be camelCase:

<Table
  rowHeight={10}
  headerBorderInset={5}
  renderRow={this.renderRow}
/>

I don’t expect to see prop names like row_height or row-height in a component’s public API. Component’s props are an object (kind of like an “option bag”), and we generally expect those options to be camelCase. This may not be always idiomatic in DOM, but the DOM is not very consistent in many places. React aligns with the JavaScript ecosystem which overwhelmingly uses camelCase.

But what about the DOM? This is where it gets thorny.

DOM Properties Are Not Just “Attributes in JS”

In DOM, we have attributes and properties. Attributes are the things you usually see in HTML. Properties are the things you usually set from JS. But crucially, DOM APIs exist both for setting properties and for setting attributes — and they’re not even always setting the same thing.

node.value = 10; // setting a property
node.setAttribute('value', '10'); // setting an attribute

In many cases it doesn’t matter. In some cases it does. But maybe not in the way one might think from using React (which has one abstraction over both).

React Is Not Just Setting Properties

A common misconception is that since React currently uses the camelCase convention for most DOM props, it means React is setting DOM properties. This is wrong.

In fact, React is currently using attributes for almost all props it supports. In some cases, like value, this is causing issues (which as I discussed we want to revert). In other cases, this is actually great — because we don’t have to include a list of supported properties into the React bundle. Using attributes under the hood is what allowed a major size reduction in React 16.

My point here is that whether React uses properties or attributes internally is an implementation detail — and has nothing to do with whether React DOM element API should be using property names, attribute names, or even some other names that make sense.

Still, Let’s Just Use Property Names?

Okay, properties and attributes are an implementation detail. But why not just standardize on using DOM property names since those were specifically made “for JavaScript”? Isn’t that how React API is designed today?

Well, not quite. Only one of the props enumerated below corresponds to a real DOM object property:

<div
  tabIndex={1}
  data-id="123"
  aria-live="polite"
  nopin="nopin"
  itemType="http://schema.org/Movie"
  onClick={function() { alert('hi') }}
/>

Ironically, the only prop above that has an actual DOM property with the same name corresponding to it (tabIndex if you weren’t sure) is actually being set by React as an attribute!

So by this point you probably see it’s neither clear-cut nor consistent. In some cases properties don’t exist (such as for custom, non-standard attributes), in some cases React could provide a richer API (data- vs dataSet) but currently doesn’t.

In some cases React intentionally chooses to deviate (onClick in React vs onclick DOM property) because it makes more sense for custom React components. This is because React components often expose more complex event handlers like onItemClick. It would be very inconsistent if you wrote

. And
isn’t camelCase, which we wanted to avoid in a component API.

Above, I explained that React already isn’t consistent about “always using DOM property name”, that React doesn’t even actually use properties internally (so that rule of thumb doesn’t describe the actual mechanics either), and that in many cases DOM properties simply don’t exist so we have to stick with allowing the attribute name.

If Not Property Names, Let’s Be Consistent and Use Attribute Names?

So why not go with using only attribute names? This could be plausible. But now we bump into the very first consideration we brought up. Using a React component should feel like idiomatic JavaScript. But often components forward at least some props to the underlying DOM element.

<Button
  borderColor='red'
  tabIndex={1}
 />

 // renders...

 <button
   tabIndex={1}
/>

It would be awkward for a custom Button to accept props with inconsistent capitalization:

<Button
  borderColor='red'
  tabindex={1}
 />

This forces the consumer to always remember if a certain prop is an actual DOM prop, or just a part of the component contract. Even that distinction is fuzzy — a component may choose to first pass a certain prop through, but then to actually start using it for some extra logic. Where do you put the boundary between “DOM props” and “other props”?

I think this is the primary reason it’s desirable for props like tabIndex, cellSpacing, and most other DOM-related props to follow the camelCase convention. It’s not because they’re DOM property names. It’s because they often end up in component APIs. And we want component APIs to be consistently camelCase.

We want to make it easy for custom components like Button to wrap and forward them without either “translating” them to the attribute name at the point where they flow into the DOM, and without introducing non-camelCase props into a custom component API.

This also explains why props like data-*, aria-*, and custom attributes are reasonable exceptions (even though we could make richer APIs for them). They are rarely passed to custom components. Typically, they are too coupled to the DOM to be useful in custom components — and instead, they become an implementation detail of something like a or a