New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React Fire: Modernizing React DOM #13525

Open
gaearon opened this Issue Aug 31, 2018 · 190 comments

Comments

Projects
None yet
@gaearon
Member

gaearon commented Aug 31, 2018

This year, the React team has mostly been focused on fundamental improvements to React.

As this work is getting closer to completion, we're starting to think of what the next major releases of React DOM should look like. There are quite a few known problems, and some of them are hard or impossible to fix without bigger internal changes.

We want to undo past mistakes that caused countless follow-up fixes and created much technical debt. We also want to remove some of the abstraction in the event system which has been virtually untouched since the first days of React, and is a source of much complexity and bundle size.

We're calling this effort "React Fire".

🔥 React Fire

React Fire is an effort to modernize React DOM. Our goal is to make React better aligned with how the DOM works, revisit some controversial past decisions that led to problems, and make React smaller and faster.

We want to ship this set of changes in a future React major release because some of them will unfortunately be breaking. Nevertheless, we think they're worth it. And we have more than 50 thousands components at Facebook to keep us honest about our migration strategy. We can't afford to rewrite product code except a few targeted fixes or automated codemods.

Strategy

There are a few different things that make up our current plan. We might add or remove something but here's the thinking so far:

  • Stop reflecting input values in the value attribute (#11896). This was originally added in React 15.2.0 via #6406. It was very commonly requested because people's conceptual model of the DOM is that the value they see in the DOM inspector should match the value JSX attribute. But that's not how the DOM works. When you type into a field, the browser doesn't update the value attribute. React shouldn't do it either. It turned out that this change, while probably helpful for some code relying on CSS selectors, caused a cascade of bugs — some of them still unfixed to this day. Some of the fallout from this change includes: #7179, #8395, #7328, #7233, #11881, #7253, #9584, #9806, #9714, #11534, #11746, #12925. At this point it's clearly not worth it to keep fighting the browser, and we should revert it. The positive part of this journey is that thanks to tireless work from our DOM contributors (@nhunzaker, @aweary, @jquense, and @philipp-spiess) we now have detailed DOM test fixtures that will help us avoid regressions.

  • Attach events at the React root rather than the document (#2043). Attaching event handlers to the document becomes an issue when embedding React apps into larger systems. The Atom editor was one of the first cases that bumped into this. Any big website also eventually develops very complex edge cases related to stopPropagation interacting with non-React code or across React roots (#8693, #8117, #12518). We will also want to attach events eagerly to every root so that we can do less runtime checks during updates.

  • Migrate from onChange to onInput and don’t polyfill it for uncontrolled components (#9657). See the linked issue for a detailed plan. It has been confusing that React uses a different event name for what's known as input event in the DOM. While we generally avoid making big changes like this without significant benefit, in this case we also want to change the behavior to remove some complexity that's only necessary for edge cases like mutating controlled inputs. So it makes sense to do these two changes together, and use that as an opportunity to make onInput and onChange work exactly how the DOM events do for uncontrolled components.

  • Drastically simplify the event system (#4751). The current event system has barely changed since its initial implementation in 2013. It is reused across React DOM and React Native, so it is unnecessarily abstract. Many of the polyfills it provides are unnecessary for modern browsers, and some of them create more issues than they solve. It also accounts for a significant portion of the React DOM bundle size. We don't have a very specific plan here, but we will probably fork the event system completely, and then see how minimal we can make it if we stick closer to what the DOM gives us. It's plausible that we'll get rid of synthetic events altogether. We should stop bubbling events like media events which don’t bubble in the DOM and don’t have a good reason to bubble. We want to retain some React-specific capabilities like bubbling through portals, but we will attempt to do this via simpler means (e.g. re-dispatching the event). Passive events will likely be a part of this.

  • classNameclass (#4331, see also #13525 (comment) below). This has been proposed countless times. We're already allowing passing class down to the DOM node in React 16. The confusion this is creating is not worth the syntax limitations it's trying to protect against. We wouldn't do this change by itself, but combined with everything else above it makes sense. Note we can’t just allow both without warnings because this makes it very difficult for a component ecosystem to handle. Each component would need to learn to handle both correctly, and there is a risk of them conflicting. Since many components process className (for example by appending to it), it’s too error-prone.

Tradeoffs

  • We can't make some of these changes if we aim to keep exposing the current private React event system APIs for projects like React Native Web. However, React Native Web will need a different strategy regardless because React Fabric will likely move more of the responder system to the native side.

  • We may need to drop compatibility with some older browsers, and/or require more standalone polyfills for them. We still care about supporting IE11 but it's possible that we will not attempt to smooth over some of the existing browser differences — which is the stance taken by many modern UI libraries.

Rollout Plan

At this stage, the project is very exploratory. We don't know for sure if all of the above things will pan out. Because the changes are significant, we will need to dogfood them at Facebook, and try them out in a gradual fashion. This means we'll introduce a feature flag, fork some of the code, and keep it enabled at Facebook for a small group of people. The open source 16.x releases will keep the old behavior, but on master you will be able to run it with the feature flag on.

I plan to work on the project myself for the most part, but I would very much appreciate more discussion and contributions from @nhunzaker, @aweary, @jquense, and @philipp-spiess who have been stellar collaborators and have largely steered React DOM while we were working on Fiber. If there's some area you're particularly interested in, please let me know and we'll work it out.

There are likely things that I missed in this plan. I'm very open to feedback, and I hope this writeup is helpful.

@matteing

This comment has been minimized.

Show comment
Hide comment
@matteing

matteing Aug 31, 2018

I love this. Reducing bundle size and the "class" prop are changes that will be very welcome.

Great work!

matteing commented Aug 31, 2018

I love this. Reducing bundle size and the "class" prop are changes that will be very welcome.

Great work!

@developit

This comment has been minimized.

Show comment
Hide comment
@developit

developit commented Aug 31, 2018

🙂

@erikras

This comment has been minimized.

Show comment
Hide comment
@erikras

erikras Aug 31, 2018

Attention form library authors! 🤣

erikras commented Aug 31, 2018

Attention form library authors! 🤣

@LaurenceM10

This comment has been minimized.

Show comment
Hide comment
@LaurenceM10

LaurenceM10 commented Aug 31, 2018

Great!

@ryanflorence

This comment has been minimized.

Show comment
Hide comment
@ryanflorence

ryanflorence Aug 31, 2018

Contributor

className → class is fantastic

What about all the others? Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.

Contributor

ryanflorence commented Aug 31, 2018

className → class is fantastic

What about all the others? Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.

@diegohaz

This comment has been minimized.

Show comment
Hide comment
@diegohaz

diegohaz Aug 31, 2018

Adopting class is a major breakthrough in making the library more friendly for beginners. Congratulations.

diegohaz commented Aug 31, 2018

Adopting class is a major breakthrough in making the library more friendly for beginners. Congratulations.

@tannerlinsley

This comment has been minimized.

Show comment
Hide comment
@tannerlinsley

tannerlinsley Aug 31, 2018

This is awesome. I'm so curious how the move to class is actually going work with props.

Seems like ({ class }) => <div class={class} /> would initially present a reserved keyword problem?

tannerlinsley commented Aug 31, 2018

This is awesome. I'm so curious how the move to class is actually going work with props.

Seems like ({ class }) => <div class={class} /> would initially present a reserved keyword problem?

@solomonhawk

This comment has been minimized.

Show comment
Hide comment
@solomonhawk

solomonhawk Aug 31, 2018

This is fantastic news, thanks @gaearon!

solomonhawk commented Aug 31, 2018

This is fantastic news, thanks @gaearon!

@felixfbecker

This comment has been minimized.

Show comment
Hide comment
@felixfbecker

felixfbecker Aug 31, 2018

I love every of these points, except the className change. It seems downright contradictory to the goal the other points are pursuing (aligning with the DOM API). React binds to DOM properties, not HTML attributes (this is this even articulated in the first point). The DOM Element property is named className, not class. So why would it be named class in React?

felixfbecker commented Aug 31, 2018

I love every of these points, except the className change. It seems downright contradictory to the goal the other points are pursuing (aligning with the DOM API). React binds to DOM properties, not HTML attributes (this is this even articulated in the first point). The DOM Element property is named className, not class. So why would it be named class in React?

@alexparish

This comment has been minimized.

Show comment
Hide comment
@alexparish

alexparish Aug 31, 2018

Fantastic! Do you have a goal for bundle size reduction?

alexparish commented Aug 31, 2018

Fantastic! Do you have a goal for bundle size reduction?

@mknepprath

This comment has been minimized.

Show comment
Hide comment
@mknepprath

mknepprath commented Aug 31, 2018

👏

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 31, 2018

Member

What about all the others? Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.

I’m open to discussion but I’d argue these changes aren’t worth it (except for maybe).

Member

gaearon commented Aug 31, 2018

What about all the others? Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.

I’m open to discussion but I’d argue these changes aren’t worth it (except for maybe).

@nhunzaker

This comment has been minimized.

Show comment
Hide comment
@nhunzaker

nhunzaker Aug 31, 2018

Collaborator

I think a re-write of the event system is the most interesting aspect of this. There is significant opportunity to reduce the bundle size and ease community contributions.

Let's do it!

Collaborator

nhunzaker commented Aug 31, 2018

I think a re-write of the event system is the most interesting aspect of this. There is significant opportunity to reduce the bundle size and ease community contributions.

Let's do it!

@kentcdodds

This comment has been minimized.

Show comment
Hide comment
@kentcdodds

kentcdodds Aug 31, 2018

Contributor

I wonder if it would be worthwhile to also work on releasing JSX 2.0 at the same time? People are going to need to re-learn a handful of things anyway. Maybe it's better to have to re-learn a bunch at one time rather than a few things twice over a period of time? Just a thought that occurred as I read this.

Contributor

kentcdodds commented Aug 31, 2018

I wonder if it would be worthwhile to also work on releasing JSX 2.0 at the same time? People are going to need to re-learn a handful of things anyway. Maybe it's better to have to re-learn a bunch at one time rather than a few things twice over a period of time? Just a thought that occurred as I read this.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 31, 2018

Member

I love every of these points, except the className change. It seems downright contradictory to the goal the other points are pursuing (aligning with the DOM API). React binds to DOM properties, not HTML attributes (this is this even articulated in the first point).

And yet if we pass an unknown key/value pair it will be treated as an attribute since React 16. So we’re already inconsistent. Also, my comment was about users being wrong in expecting React to set value attribute. Whether React API uses attribute name or property name in its API is compeletely orthogonal.

I’ve defended your side of this argument for years but I think now that this is friction that’s just not worth it. You don’t gain anything from it. Just letting people use class has no negative effects except it doesn’t work with destructuring, and the migration cost. Everybody complains about it when they learn React. I think doing what people expect in this case is more important than being pedantic. And since we’re changing other DOM things anyway, let’s batch this together.

Member

gaearon commented Aug 31, 2018

I love every of these points, except the className change. It seems downright contradictory to the goal the other points are pursuing (aligning with the DOM API). React binds to DOM properties, not HTML attributes (this is this even articulated in the first point).

And yet if we pass an unknown key/value pair it will be treated as an attribute since React 16. So we’re already inconsistent. Also, my comment was about users being wrong in expecting React to set value attribute. Whether React API uses attribute name or property name in its API is compeletely orthogonal.

I’ve defended your side of this argument for years but I think now that this is friction that’s just not worth it. You don’t gain anything from it. Just letting people use class has no negative effects except it doesn’t work with destructuring, and the migration cost. Everybody complains about it when they learn React. I think doing what people expect in this case is more important than being pedantic. And since we’re changing other DOM things anyway, let’s batch this together.

@erikras

This comment has been minimized.

Show comment
Hide comment
@erikras

erikras Aug 31, 2018

As long as React Fire is blazing fast.... 👍

erikras commented Aug 31, 2018

As long as React Fire is blazing fast.... 👍

@kentcdodds

This comment has been minimized.

Show comment
Hide comment
@kentcdodds

kentcdodds Aug 31, 2018

Contributor

These changes are all fantastic. I'm way excited about this and its implications for react-testing-library. In particular, events being bound to the react root (or maybe even drop event delegation altogether as it may not be necessary in modern environments anymore?), potentially removing/rewriting synthetic events, and onChange -> onInput will seriously improve the implementation of react-testing-library and the experience in using the tool.

I'd love to provide feedback on this as it's being implemented.

Contributor

kentcdodds commented Aug 31, 2018

These changes are all fantastic. I'm way excited about this and its implications for react-testing-library. In particular, events being bound to the react root (or maybe even drop event delegation altogether as it may not be necessary in modern environments anymore?), potentially removing/rewriting synthetic events, and onChange -> onInput will seriously improve the implementation of react-testing-library and the experience in using the tool.

I'd love to provide feedback on this as it's being implemented.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 31, 2018

Member

maybe even drop event delegation altogether as it may not be necessary in modern environments anymore

We considered this but think this might be an oversimplification. Event delegation lets us avoid setting up a bunch of listeners for every node on the initial render. And swapping them on updates. Those aspects shouldn’t be ignored. There is likely more benchmarking to be done there though.

Member

gaearon commented Aug 31, 2018

maybe even drop event delegation altogether as it may not be necessary in modern environments anymore

We considered this but think this might be an oversimplification. Event delegation lets us avoid setting up a bunch of listeners for every node on the initial render. And swapping them on updates. Those aspects shouldn’t be ignored. There is likely more benchmarking to be done there though.

@drcmda

This comment has been minimized.

Show comment
Hide comment
@drcmda

drcmda Aug 31, 2018

@tannerlinsley ({ class: className }) => <div class={className} /> unfortunately this will kill jsx 2.0 object short hand notation (<div class />), but so be it ...

It would be super, super nice if class could finally take objects and arrays btw next to strings.

drcmda commented Aug 31, 2018

@tannerlinsley ({ class: className }) => <div class={className} /> unfortunately this will kill jsx 2.0 object short hand notation (<div class />), but so be it ...

It would be super, super nice if class could finally take objects and arrays btw next to strings.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Aug 31, 2018

Member

Do you have a goal for bundle size reduction?

Dropping a third of React DOM would be nice. We’ll see. It’s hard to say early but we’ll do our best.

Member

gaearon commented Aug 31, 2018

Do you have a goal for bundle size reduction?

Dropping a third of React DOM would be nice. We’ll see. It’s hard to say early but we’ll do our best.

@AlexGalays

This comment has been minimized.

Show comment
Hide comment
@AlexGalays

AlexGalays Aug 31, 2018

Wow, this is an enumeration of almost all the design decisions I mention when people ask me about React cons.

Love the direction this is going.

AlexGalays commented Aug 31, 2018

Wow, this is an enumeration of almost all the design decisions I mention when people ask me about React cons.

Love the direction this is going.

@jacobp100

This comment has been minimized.

Show comment
Hide comment
@jacobp100

jacobp100 Aug 31, 2018

What would the upgrade path be for libraries that use className and want to support multiple versions of React?

jacobp100 commented Aug 31, 2018

What would the upgrade path be for libraries that use className and want to support multiple versions of React?

@ryanflorence

This comment has been minimized.

Show comment
Hide comment
@ryanflorence

ryanflorence Aug 31, 2018

Contributor

@gaearon Maybe it's not a big deal, today its nice to say "props are DOM properties not HTML attributes", now it'll be "except className, that one is the HTML name". I'd also like to be able to copy/paste SVG w/o 10 minutes of messing around with attributes to match up with React's

Contributor

ryanflorence commented Aug 31, 2018

@gaearon Maybe it's not a big deal, today its nice to say "props are DOM properties not HTML attributes", now it'll be "except className, that one is the HTML name". I'd also like to be able to copy/paste SVG w/o 10 minutes of messing around with attributes to match up with React's

@fforw

This comment has been minimized.

Show comment
Hide comment
@fforw

fforw Aug 31, 2018

Contributor

What about htmlFor?

Contributor

fforw commented Aug 31, 2018

What about htmlFor?

@phpnode

This comment has been minimized.

Show comment
Hide comment
@phpnode

phpnode Aug 31, 2018

It feels like the className -> class transition will have to be done very carefully, probably over an extended period of time. It's going to cause a lot of issues for the ecosystem, as virtually every component will need to be changed - even very trivial ones. This is mostly fine if you "own" the code and there's a codemod, but when you're dependent on 3rd party libraries you're largely at the mercy of maintainers.

The rest of the changes seem relatively low risk from an ecosystem point of view, but getting rid of className will really cause a lot of pain. It seems like it should be possible to split that into a separate issue and release it on a different schedule to the rest of the 🔥 changes.

phpnode commented Aug 31, 2018

It feels like the className -> class transition will have to be done very carefully, probably over an extended period of time. It's going to cause a lot of issues for the ecosystem, as virtually every component will need to be changed - even very trivial ones. This is mostly fine if you "own" the code and there's a codemod, but when you're dependent on 3rd party libraries you're largely at the mercy of maintainers.

The rest of the changes seem relatively low risk from an ecosystem point of view, but getting rid of className will really cause a lot of pain. It seems like it should be possible to split that into a separate issue and release it on a different schedule to the rest of the 🔥 changes.

@Pajn

This comment has been minimized.

Show comment
Hide comment
@Pajn

Pajn Aug 31, 2018

I agree with @felixfbecker
Everything sounds awesome and would improve quality for both developers and users, but the className change.

Beeing able to deconstruct all properties but one would certainly cause more confusion and be harder to explain to new users than that they need to use className because class is a keyword (which they can clearly see when the misstake is made, as it's colored differently). Working around class in deconstructing requires introducing new syntax or a completely different way to read out that specific property that would only work untill you need to use a rest property.
It creates many problems just to save four characters.

Pajn commented Aug 31, 2018

I agree with @felixfbecker
Everything sounds awesome and would improve quality for both developers and users, but the className change.

Beeing able to deconstruct all properties but one would certainly cause more confusion and be harder to explain to new users than that they need to use className because class is a keyword (which they can clearly see when the misstake is made, as it's colored differently). Working around class in deconstructing requires introducing new syntax or a completely different way to read out that specific property that would only work untill you need to use a rest property.
It creates many problems just to save four characters.

@caub

This comment has been minimized.

Show comment
Hide comment
@caub

caub Aug 31, 2018

@felixfbecker it could it be class/for in JSX and className/htmlFor in the JS version?

<label class="foo" for="bar">..</label>

would be

React.createElement('label', {className: 'foo', htmlFor: 'bar'}, '..')

caub commented Aug 31, 2018

@felixfbecker it could it be class/for in JSX and className/htmlFor in the JS version?

<label class="foo" for="bar">..</label>

would be

React.createElement('label', {className: 'foo', htmlFor: 'bar'}, '..')
@alexeybondarenko

This comment has been minimized.

Show comment
Hide comment
@alexeybondarenko

alexeybondarenko Aug 31, 2018

Great plan! Nice to here that! 👏👏👏

alexeybondarenko commented Aug 31, 2018

Great plan! Nice to here that! 👏👏👏

@eloytoro

This comment has been minimized.

Show comment
Hide comment
@eloytoro

eloytoro Sep 4, 2018

"The behavior you see a tool being used for is a behavior that tool encourages"
~ Gary Bernhardt

The way React is being used, for most of it not being part of React's API, results in a high number of pitfalls that result discussions like this where everyone is confused and frustrated. Is using a reserved keyword such as class bad because of javascript, html or react itself?

Going back to the changes made in 16.3 (If I remember correctly) the library would now behave differently by accepting any attribute passed to native html elements being rendered by the vdom, I remember quite clearly that most of my codebase was now plagued by bugs because of the behavior we had implementing this clearly bad pattern

<div {...this.props} />

So I gotta say im quite disappointed that Dan is suggesting that we keep spreading this terrible code across all codebases.

First of all, there are no guarantees in the contents of props, but at the first time native html elements will almost always not do whats expected of them if you pass the wrong props to it, however at the same time its considered to be safe to propagate parent props down to child components, but in reality this is terribly lazy

<Child {...props} />

const Child = (props) => <div {...props} />

Sometimes you don't even know what you're rendering and this issue becomes even harder to deal with. It is also extremely hard to typecheck these kind of components using type systems because that would mean that most components are required to implement a number of random interfaces they should share between each other, its a deep deep rabbit hole to go down into.

If a rewrite to react is made to fix this I would expect the designers behind it to realize the harm these bad patterns cause to codebase instead of doubling down on bad decisions.

We gotta remember that jsx is built on top of javascript, and using javascript's "idiosyncrasies" as excuses to deliberately conflict with the language in the use of keywords is a bad first step towards fixing the API, I would suggest that instead of starting a war against the language you would move away from it entirely

eloytoro commented Sep 4, 2018

"The behavior you see a tool being used for is a behavior that tool encourages"
~ Gary Bernhardt

The way React is being used, for most of it not being part of React's API, results in a high number of pitfalls that result discussions like this where everyone is confused and frustrated. Is using a reserved keyword such as class bad because of javascript, html or react itself?

Going back to the changes made in 16.3 (If I remember correctly) the library would now behave differently by accepting any attribute passed to native html elements being rendered by the vdom, I remember quite clearly that most of my codebase was now plagued by bugs because of the behavior we had implementing this clearly bad pattern

<div {...this.props} />

So I gotta say im quite disappointed that Dan is suggesting that we keep spreading this terrible code across all codebases.

First of all, there are no guarantees in the contents of props, but at the first time native html elements will almost always not do whats expected of them if you pass the wrong props to it, however at the same time its considered to be safe to propagate parent props down to child components, but in reality this is terribly lazy

<Child {...props} />

const Child = (props) => <div {...props} />

Sometimes you don't even know what you're rendering and this issue becomes even harder to deal with. It is also extremely hard to typecheck these kind of components using type systems because that would mean that most components are required to implement a number of random interfaces they should share between each other, its a deep deep rabbit hole to go down into.

If a rewrite to react is made to fix this I would expect the designers behind it to realize the harm these bad patterns cause to codebase instead of doubling down on bad decisions.

We gotta remember that jsx is built on top of javascript, and using javascript's "idiosyncrasies" as excuses to deliberately conflict with the language in the use of keywords is a bad first step towards fixing the API, I would suggest that instead of starting a war against the language you would move away from it entirely

@syranide

This comment has been minimized.

Show comment
Hide comment
@syranide

syranide Sep 4, 2018

Contributor

@gaearon Long time! :) (ps. sorry for tl;dr ahead)

Anyway, I just wanted to give some insight into what I've learned from my own adventures. I've made my own "bare minimal" React derivative which I now use for performance/feature critical applications. It was initially just a fun side experiment but I was seeing performance improvements of beyond +300% in real-world cases for initial rendering/updating/deleting including many others benefits. That's without using a pooled event handler too.

IIRC a large portion of this comes simply from my own library bypassing all the React-props logic and simply feeding attributes straight into setAttribute, styles straight into style.setProperty, listeners straight into addEventListener, and so on. So the basic DOM element interface looks like this `{attributes: {}, style: {}, listeners: {}, className: "", ...}, it's more verbose but the interface is now small and trivial to implement and is extremely fast. This is probably too extreme for you, but it has served me very very well.

Something I've been doing as well is dropping CSS entirely, all styles are inline. It's much much faster than you would expect and comes with a lot of nice practical benefits, it also side-steps the cost of browser initial CSS-parsing (which is surprisingly expensive) and selector matching which recoups most of the overhead. I can easily achieve stable 60 FPS and way beyond on my under-powered laptop for even complex hierarchies (without any tree pruning).

I imagine most of that is not really useful for you, but maybe there's something interesting there. Anyway, my main gripe with HTML+React is that React user components do not expose any interface for layouting/positioning (and probably to some extent also events) without resorting to CSS. To put it simply, if I create a ButtonComponent and then use it, by default it cannot be positioned or integrated into a layout without; implementing purpose-special logic inside it, wrapping it in a dummy element which can be positioned or by exposing a style-prop which is merged into the styles used for ButtonComponent root element. None of those are perfect, and merging styles as-is is dangerous as it's easy to put styles in there that you shouldn't. Essentially, the problem is that at component boundaries, a subset of the available properties should be public (positioning/layouting) and some internal (visual styling).

Another thing I do is separation between components and content/children. E.g. DOM components do not take a list of children, instead it accepts a content instance. The content instance implements the reconciliation logic and rendering of children, which means that you can have different implementations for different purposes. The most obvious use-cases being; mostly static hierarchies vs dynamically generated children. So static hierarchies which make up the majority can be implemented using faster simpler logic, whereas dynamically generated children may have configurable strategies. It also opens up for the possibility of having "content components" which could be used for intelligently managing e.g. flexbox layouts without unnecessary indirection.

Related to that and something I think React gets wrong is children, I haven't arrived at specific conclusion or solution yet. But I strongly believe there's a fundamental problem in React in that you cannot bubble props from the root to an arbitrary point in the hierarchy without re-rendering every single element between those two points and effectively pruning is often problematic too. Performance is mostly "good enough" and the added complexity of managing this in React might not make it worth solving. But in my experiments so far I've been able to reduce full root update costs to mere fractions with very small amounts of trivial code, but my intuition says it's not very compatible with the philosophy of React or its simplistic children model.

Something that's mostly definitely not for you, but a big feature of my library is that it basically only provides a bare minimal "base DOM element" for general purpose. The idea being that you can trivially extend/replace and implement your own low-level specific behaviors if you're developing a large project with specific requirements/circumstances very cheaply and without resorting to "hacks", e.g. if you're very heavily touch-oriented, want to do special style-handling, etc. It also means that different versions are compatible and can be run side-by-side as long as the ABI is the same (a change should be extremely rare and very simple to fix). This also means that you can be quite opinionated in the DOM element interface without having to be all-encompassing and try to solve everyone's problem.

Anyway, perhaps mostly just rambling and I'm probably not conveying most concepts very well, but maybe there's something interesting information for you from someone who went the opposite direction. Most of it is probably not relevant to you simply because you are targeting "simplicity", but who knows :)

Also before I forget, you might be interested in my objectKeyValueReconcile function, specifically optimized to be exceedingly fast for comparing prev/next props for React-like scenarios. It's a responsible for quite significant real-world performance gains for me.

https://github.com/syranide/surgical/blob/master/packages/surgical/private/objectKeyValueReconcile.js

Contributor

syranide commented Sep 4, 2018

@gaearon Long time! :) (ps. sorry for tl;dr ahead)

Anyway, I just wanted to give some insight into what I've learned from my own adventures. I've made my own "bare minimal" React derivative which I now use for performance/feature critical applications. It was initially just a fun side experiment but I was seeing performance improvements of beyond +300% in real-world cases for initial rendering/updating/deleting including many others benefits. That's without using a pooled event handler too.

IIRC a large portion of this comes simply from my own library bypassing all the React-props logic and simply feeding attributes straight into setAttribute, styles straight into style.setProperty, listeners straight into addEventListener, and so on. So the basic DOM element interface looks like this `{attributes: {}, style: {}, listeners: {}, className: "", ...}, it's more verbose but the interface is now small and trivial to implement and is extremely fast. This is probably too extreme for you, but it has served me very very well.

Something I've been doing as well is dropping CSS entirely, all styles are inline. It's much much faster than you would expect and comes with a lot of nice practical benefits, it also side-steps the cost of browser initial CSS-parsing (which is surprisingly expensive) and selector matching which recoups most of the overhead. I can easily achieve stable 60 FPS and way beyond on my under-powered laptop for even complex hierarchies (without any tree pruning).

I imagine most of that is not really useful for you, but maybe there's something interesting there. Anyway, my main gripe with HTML+React is that React user components do not expose any interface for layouting/positioning (and probably to some extent also events) without resorting to CSS. To put it simply, if I create a ButtonComponent and then use it, by default it cannot be positioned or integrated into a layout without; implementing purpose-special logic inside it, wrapping it in a dummy element which can be positioned or by exposing a style-prop which is merged into the styles used for ButtonComponent root element. None of those are perfect, and merging styles as-is is dangerous as it's easy to put styles in there that you shouldn't. Essentially, the problem is that at component boundaries, a subset of the available properties should be public (positioning/layouting) and some internal (visual styling).

Another thing I do is separation between components and content/children. E.g. DOM components do not take a list of children, instead it accepts a content instance. The content instance implements the reconciliation logic and rendering of children, which means that you can have different implementations for different purposes. The most obvious use-cases being; mostly static hierarchies vs dynamically generated children. So static hierarchies which make up the majority can be implemented using faster simpler logic, whereas dynamically generated children may have configurable strategies. It also opens up for the possibility of having "content components" which could be used for intelligently managing e.g. flexbox layouts without unnecessary indirection.

Related to that and something I think React gets wrong is children, I haven't arrived at specific conclusion or solution yet. But I strongly believe there's a fundamental problem in React in that you cannot bubble props from the root to an arbitrary point in the hierarchy without re-rendering every single element between those two points and effectively pruning is often problematic too. Performance is mostly "good enough" and the added complexity of managing this in React might not make it worth solving. But in my experiments so far I've been able to reduce full root update costs to mere fractions with very small amounts of trivial code, but my intuition says it's not very compatible with the philosophy of React or its simplistic children model.

Something that's mostly definitely not for you, but a big feature of my library is that it basically only provides a bare minimal "base DOM element" for general purpose. The idea being that you can trivially extend/replace and implement your own low-level specific behaviors if you're developing a large project with specific requirements/circumstances very cheaply and without resorting to "hacks", e.g. if you're very heavily touch-oriented, want to do special style-handling, etc. It also means that different versions are compatible and can be run side-by-side as long as the ABI is the same (a change should be extremely rare and very simple to fix). This also means that you can be quite opinionated in the DOM element interface without having to be all-encompassing and try to solve everyone's problem.

Anyway, perhaps mostly just rambling and I'm probably not conveying most concepts very well, but maybe there's something interesting information for you from someone who went the opposite direction. Most of it is probably not relevant to you simply because you are targeting "simplicity", but who knows :)

Also before I forget, you might be interested in my objectKeyValueReconcile function, specifically optimized to be exceedingly fast for comparing prev/next props for React-like scenarios. It's a responsible for quite significant real-world performance gains for me.

https://github.com/syranide/surgical/blob/master/packages/surgical/private/objectKeyValueReconcile.js

@bazjapan

This comment has been minimized.

Show comment
Hide comment
@bazjapan

bazjapan Sep 4, 2018

imho I'd drop the "class" idea. JSX will eventually die.

bazjapan commented Sep 4, 2018

imho I'd drop the "class" idea. JSX will eventually die.

@iamdustan

This comment has been minimized.

Show comment
Hide comment
@iamdustan

iamdustan Sep 4, 2018

Contributor

What are the thoughts of adding support for non-element based DOM/window events to React DOM? With the possible removal of synthetic events I suspect there’d still be some form of event collecting/updating/batching. The keypress, resize, and window scroll events are common scenarios that come to mind, but possibly being able to support most/all of the list in https://developer.mozilla.org/en-US/docs/Web/Events would be beneficial behind the same abstraction.

This happens to also be discussed in the oldest open issue #285 😄.

Contributor

iamdustan commented Sep 4, 2018

What are the thoughts of adding support for non-element based DOM/window events to React DOM? With the possible removal of synthetic events I suspect there’d still be some form of event collecting/updating/batching. The keypress, resize, and window scroll events are common scenarios that come to mind, but possibly being able to support most/all of the list in https://developer.mozilla.org/en-US/docs/Web/Events would be beneficial behind the same abstraction.

This happens to also be discussed in the oldest open issue #285 😄.

@kans

This comment has been minimized.

Show comment
Hide comment
@kans

kans Sep 4, 2018

@gaearon

The practical effect of className -> class will be:

({ className }) => <div className={className} />

will become

({ ...rest }) => <div class={rest.class} />

This change will cause quite a bit of pain while the benefits are entirely academic. We have seen other communities do similar things - eg, consider that python3 was first released a decade ago and we are still arguing about it. A sizeable portion of the community has not made the transition and never will. Please reconsider.

kans commented Sep 4, 2018

@gaearon

The practical effect of className -> class will be:

({ className }) => <div className={className} />

will become

({ ...rest }) => <div class={rest.class} />

This change will cause quite a bit of pain while the benefits are entirely academic. We have seen other communities do similar things - eg, consider that python3 was first released a decade ago and we are still arguing about it. A sizeable portion of the community has not made the transition and never will. Please reconsider.

@j-f1

This comment has been minimized.

Show comment
Hide comment
@j-f1

j-f1 Sep 4, 2018

@kans

Or this:

props => <div class={props.class} />

That’s of similar complexity to the original function.

j-f1 commented Sep 4, 2018

@kans

Or this:

props => <div class={props.class} />

That’s of similar complexity to the original function.

@arstin

This comment has been minimized.

Show comment
Hide comment
@arstin

arstin Sep 4, 2018

I liked className because class is a keyword in js, but don't think it's that big a deal either way. I prefer styling with something like glamorous, so, this isn't something that affects me. I've maybe used className a couple dozen times the past few years?? Almost never.

Though one thought that occurred to me in favor of class is that most people who actually make use of className are using css, and they expect the html/css model of class. The overwhelmingly main purpose of this prop really is just linking with css code, right, so why not just call it class like an html/css user expects?

IMO outside of design system code, really idiomatic React development wouldn't typically be using className or class very much anyway. When I do need to use classes for css, it's mostly isolated to a few small UI components. Pretty much anything used at the application layer is higher level (like, say, <Columns verticalAlign="center"> or <BodyText />).

So I guess I wonder if renaming from className to class makes it easier for the kind of people who use the prop anyway (easier to get into React development, to switch contexts, to not get angry, etc), why not just rename it?

arstin commented Sep 4, 2018

I liked className because class is a keyword in js, but don't think it's that big a deal either way. I prefer styling with something like glamorous, so, this isn't something that affects me. I've maybe used className a couple dozen times the past few years?? Almost never.

Though one thought that occurred to me in favor of class is that most people who actually make use of className are using css, and they expect the html/css model of class. The overwhelmingly main purpose of this prop really is just linking with css code, right, so why not just call it class like an html/css user expects?

IMO outside of design system code, really idiomatic React development wouldn't typically be using className or class very much anyway. When I do need to use classes for css, it's mostly isolated to a few small UI components. Pretty much anything used at the application layer is higher level (like, say, <Columns verticalAlign="center"> or <BodyText />).

So I guess I wonder if renaming from className to class makes it easier for the kind of people who use the prop anyway (easier to get into React development, to switch contexts, to not get angry, etc), why not just rename it?

strangekai pushed a commit to strangekai/rsvp-app-react that referenced this issue Sep 5, 2018

Kai Macmaster
Update the design
This is the initial html - imported into react.

Note: React is moving from className to class. It does this by rendering anything in JSX to the DOM. See facebook/react#13525 for more details.
@hkongm

This comment has been minimized.

Show comment
Hide comment
@hkongm

hkongm Sep 5, 2018

Reduce the bundle size please.

hkongm commented Sep 5, 2018

Reduce the bundle size please.

@AlexGalays

This comment has been minimized.

Show comment
Hide comment
@AlexGalays

AlexGalays Sep 6, 2018

Since this is a big, breaking update, perhaps we could fix React lifecycle naming too!

i.e shouldComponentUpdate => shouldUpdate, etc. This naming is beyond silly.

AlexGalays commented Sep 6, 2018

Since this is a big, breaking update, perhaps we could fix React lifecycle naming too!

i.e shouldComponentUpdate => shouldUpdate, etc. This naming is beyond silly.

@sompylasar

This comment has been minimized.

Show comment
Hide comment
@sompylasar

sompylasar Sep 6, 2018

Contributor

@AlexGalays

Since this is a big, breaking update, perhaps we could fix React lifecycle naming too!

i.e shouldComponentUpdate => shouldUpdate, etc. This naming is beyond silly.

Longer lifecycle hook names are less ambiguous when searched for. Shorter names are too generic and depend on the context where they are located, so may coincidentally match something else a large codebase (false positive match).

Contributor

sompylasar commented Sep 6, 2018

@AlexGalays

Since this is a big, breaking update, perhaps we could fix React lifecycle naming too!

i.e shouldComponentUpdate => shouldUpdate, etc. This naming is beyond silly.

Longer lifecycle hook names are less ambiguous when searched for. Shorter names are too generic and depend on the context where they are located, so may coincidentally match something else a large codebase (false positive match).

@ljharb

This comment has been minimized.

Show comment
Hide comment
@ljharb

ljharb Sep 6, 2018

Contributor

Lifecycle methods are part of React core, and this issue is solely about react-dom.

Contributor

ljharb commented Sep 6, 2018

Lifecycle methods are part of React core, and this issue is solely about react-dom.

@AlexGalays

This comment has been minimized.

Show comment
Hide comment
@AlexGalays

AlexGalays Sep 6, 2018

@ljharb
Oops, indeed !

@sompylasar
Sorry, that's simply not a good reason (pun intended, reasonReact fixed the method names :)). We don't prefix all our modules by their package names as well and we find them just fine. Anyway, going to stop talking about this since react-core is out of scope in this issue.

AlexGalays commented Sep 6, 2018

@ljharb
Oops, indeed !

@sompylasar
Sorry, that's simply not a good reason (pun intended, reasonReact fixed the method names :)). We don't prefix all our modules by their package names as well and we find them just fine. Anyway, going to stop talking about this since react-core is out of scope in this issue.

@askbeka

This comment has been minimized.

Show comment
Hide comment
@askbeka

askbeka Sep 7, 2018

Would love to see these changes.
I hope they will be implemented with custom-elements interop in mind.

askbeka commented Sep 7, 2018

Would love to see these changes.
I hope they will be implemented with custom-elements interop in mind.

@Daniel15

This comment has been minimized.

Show comment
Hide comment
@Daniel15

Daniel15 Sep 8, 2018

Member

I wonder if it's worth moving some of the discussions to separate threads in the forum (https://discuss.reactjs.org)? Since GitHub issues aren't threaded like forum posts, it makes it tricky to discuss multiple different things in the same issue.

Member

Daniel15 commented Sep 8, 2018

I wonder if it's worth moving some of the discussions to separate threads in the forum (https://discuss.reactjs.org)? Since GitHub issues aren't threaded like forum posts, it makes it tricky to discuss multiple different things in the same issue.

@MAPESO

This comment has been minimized.

Show comment
Hide comment
@MAPESO

MAPESO Sep 8, 2018

The changed of className -> class seems interesting to me 😺

MAPESO commented Sep 8, 2018

The changed of className -> class seems interesting to me 😺

@yordis

This comment has been minimized.

Show comment
Hide comment
@yordis

yordis Sep 9, 2018

Watch, 2019 React projects

const {
  class: className, // YouKnowIamRight PepeHands
  someFancyProp,
  ...restProps
} = props

I see this coming for sure.

I totally love to be able to copy paste HTML and not having to change className but at the same time I see the issues that class reserved keyword could bring.

At this point, I would prefer to stick with className unless you have a strong engineering concern about it (not personal preference)

But that is just my pragmatic opinion.

yordis commented Sep 9, 2018

Watch, 2019 React projects

const {
  class: className, // YouKnowIamRight PepeHands
  someFancyProp,
  ...restProps
} = props

I see this coming for sure.

I totally love to be able to copy paste HTML and not having to change className but at the same time I see the issues that class reserved keyword could bring.

At this point, I would prefer to stick with className unless you have a strong engineering concern about it (not personal preference)

But that is just my pragmatic opinion.

@benlk benlk referenced this issue Sep 9, 2018

Open

React is dropping className for class #47

0 of 3 tasks complete
@alexeyraspopov

This comment has been minimized.

Show comment
Hide comment
@alexeyraspopov

alexeyraspopov Sep 9, 2018

Contributor

While I understand the additional friction of not being able to destruct class identifier, I feel like too many people in this thread overestimate the number of times they need to receive class name as a prop.

Passing className to a React component (that is not just DOM element) means you either making quite small building blocks without providing any new abstractions or that component expose some of its implementation details. In the first case (e.g. <Button /> component) you probably would want to follow @gaearon's example and forward the "rest" of props to the DOM element as well. In the second case, maybe the additional friction would force you to avoid doing the thing and come up with better solution.

From my humble experience, I had to copy paste HTML to JSX (and replace class with className) more times than I can remember making a component that receives className.

Contributor

alexeyraspopov commented Sep 9, 2018

While I understand the additional friction of not being able to destruct class identifier, I feel like too many people in this thread overestimate the number of times they need to receive class name as a prop.

Passing className to a React component (that is not just DOM element) means you either making quite small building blocks without providing any new abstractions or that component expose some of its implementation details. In the first case (e.g. <Button /> component) you probably would want to follow @gaearon's example and forward the "rest" of props to the DOM element as well. In the second case, maybe the additional friction would force you to avoid doing the thing and come up with better solution.

From my humble experience, I had to copy paste HTML to JSX (and replace class with className) more times than I can remember making a component that receives className.

@philipwhiuk

This comment has been minimized.

Show comment
Hide comment
@philipwhiuk

philipwhiuk Sep 9, 2018

Spreading all props to a HTML is an anti pattern. If someone in the future includes an extra prop that happens to match a HTML attribute it will unexpectedly change the behaviour. If IE adds a new attribute you are hosed.

philipwhiuk commented Sep 9, 2018

Spreading all props to a HTML is an anti pattern. If someone in the future includes an extra prop that happens to match a HTML attribute it will unexpectedly change the behaviour. If IE adds a new attribute you are hosed.

@j-f1

This comment has been minimized.

Show comment
Hide comment
@j-f1

j-f1 Sep 9, 2018

@philipwhiuk if you only spread the props you don’t use there won’t be any behavior problems.

j-f1 commented Sep 9, 2018

@philipwhiuk if you only spread the props you don’t use there won’t be any behavior problems.

@mgol

This comment has been minimized.

Show comment
Hide comment
@mgol

mgol Sep 9, 2018

Contributor

@j-f1

if you only spread the props you don’t use there won’t be any behavior problems.

Not really. If you add a new option to your component API then this option stops being passed to the underlying DOM node. Someone may depend on that exact attribute being set on the element and then you break that person. Spreading all unused props to anything underneath means any addition to your component API is a breaking change.

Contributor

mgol commented Sep 9, 2018

@j-f1

if you only spread the props you don’t use there won’t be any behavior problems.

Not really. If you add a new option to your component API then this option stops being passed to the underlying DOM node. Someone may depend on that exact attribute being set on the element and then you break that person. Spreading all unused props to anything underneath means any addition to your component API is a breaking change.

@yordis

This comment has been minimized.

Show comment
Hide comment
@yordis

yordis Sep 9, 2018

Btw, I used a random example where you deconstruct the value of class but don't get cut off on that detail.

Many of you (yes, you do) deconstruct almost every value from the props and state instead of using the dot notation for non-reasons other than avoiding to type some extra text.

So that is why I am making the emphasis on the situation, but do not go sideways and focusing in spreading props or whichever another side topic you could talk about it.

If you are a little bit more pragmatic you would talk about the technical implications instead of your personal preferences but I don't see anyone saying anything about technical implications of className.

Another thing you need to pay attention to is that you are making the Core contributors focus on something that is the matter of personal preferences (prove me wrong, just because I want you to think about it pragmatically)

Their time, effort, and money (from the company) are limited so better if the community understand that there are better things to focus our attention to and if anyone from the Core team will expend an hour on something we prefer that they used it in the next big thing for React, no refactoring code for such of change.

But again, my pragmatic opinion.

yordis commented Sep 9, 2018

Btw, I used a random example where you deconstruct the value of class but don't get cut off on that detail.

Many of you (yes, you do) deconstruct almost every value from the props and state instead of using the dot notation for non-reasons other than avoiding to type some extra text.

So that is why I am making the emphasis on the situation, but do not go sideways and focusing in spreading props or whichever another side topic you could talk about it.

If you are a little bit more pragmatic you would talk about the technical implications instead of your personal preferences but I don't see anyone saying anything about technical implications of className.

Another thing you need to pay attention to is that you are making the Core contributors focus on something that is the matter of personal preferences (prove me wrong, just because I want you to think about it pragmatically)

Their time, effort, and money (from the company) are limited so better if the community understand that there are better things to focus our attention to and if anyone from the Core team will expend an hour on something we prefer that they used it in the next big thing for React, no refactoring code for such of change.

But again, my pragmatic opinion.

@AlexGalays

This comment has been minimized.

Show comment
Hide comment
@AlexGalays

AlexGalays Sep 16, 2018

Speaking of polyfills, I don't think react should try to detect whether the user specified "good" polyfills.
(e.g hasBadMapPolyfill in the code)
React shouldn't be held responsible for all the random mistakes one can make in a programming project.

AlexGalays commented Sep 16, 2018

Speaking of polyfills, I don't think react should try to detect whether the user specified "good" polyfills.
(e.g hasBadMapPolyfill in the code)
React shouldn't be held responsible for all the random mistakes one can make in a programming project.

@aweary

This comment has been minimized.

Show comment
Hide comment
@aweary

aweary Sep 16, 2018

Member

@AlexGalays React relies on using a Map implementation with correct semantics. If users are using a non-compliant polyfill, their app may break in unexpected and confusing ways, so its helpful to warn ahead of time in that case.

This is only done in the development build as well, so there's no overhead for this in production.

Member

aweary commented Sep 16, 2018

@AlexGalays React relies on using a Map implementation with correct semantics. If users are using a non-compliant polyfill, their app may break in unexpected and confusing ways, so its helpful to warn ahead of time in that case.

This is only done in the development build as well, so there's no overhead for this in production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment