Closed gaearon closed 4 years ago
I love this. Reducing bundle size and the "class" prop are changes that will be very welcome.
Great work!
🙂
Attention form library authors! 🤣
Great!
className → class is fantastic
What about all the others? Seems weird to still be doing clipPath
, htmlFor
, tabIndex
, etc.
Adopting class
is a major breakthrough in making the library more friendly for beginners. Congratulations.
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?
This is fantastic news, thanks @gaearon!
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?
Fantastic! Do you have a goal for bundle size reduction?
👏
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).
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!
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.
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.
As long as React Fire is blazing fast.... 👍
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.
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.
@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.
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.
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.
What would the upgrade path be for libraries that use className
and want to support multiple versions of React?
@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
What about htmlFor
?
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.
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.
@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'}, '..')
Great plan! Nice to here that! 👏👏👏
This is awesome, I can't wait to dig into the new stuff.
Drastically simplify the event system (#4751).
Now react can not delegate handler to custom elements, without extending ReactDOM definition. https://github.com/facebook/react/issues/9242
It means React can not set custom handler on custom element like <x-component onMyEvent={ev => {...}} />
@gaearon Do you have any plan about this?
All these changes are excellent but the biggest, meta change of them all is the size reduction in my opinion. React has a mean DX, but it's fat enough that downloading and parsing it on average networks and in particular mobile is a problem.
We could have it all!
Would rewriting the event delegation open the door for fixing #1254; where some events degrade perf for the node they are attached to (which for React means the whole app).
Also, as a long shot, have you considered having synthetic dataSet props? Not being able to type allowable HTML props (because of data-*) leads to a ton of bugs when forwarding props to the DOM nodes. Currently typed React components have to choose between exact types for props and allowing data attributes.
I'm excited
@ryanflorence A bit offtop but it's kinda sad that no one (as far as I know of) had the idea to make a html/css/svg -> jsx transfomer in order to ease migrations to React with so many trivial changes to map HTML attrs to React props. So many man-hours wasted perfoming mostly find-and-replace :(
It's very strange to see the following in the same issue (and the comments):
Our goal is to make React better aligned with how the DOM works className → class Seems weird to still be doing clipPath, htmlFor, tabIndex, etc.
When all these are direct counterparts of DOM APIs
And this argument doesn't pass muster:
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
React has always been just a very thin layer on top of JS. So everything else besides JSX's angle brackets was JS and had a direct counterpart in DOM APIs for things directly related to DOM: className, clipPath, htmlFor, tabIndex etc. etc. etc.
With the decision to introduce class
React stops being a thin layer (class
is a reserved word in JS) and breaks away from the declared goal of being more compatible with DOM APIs.
It's especially jarring to see that you want to both "Migrate from onChange to onInput" because it's inconsistent with the DOM and become inconsistent with the DOM by migrating className
-> class
.
Additionally, this opens a full can of worms, as people will demand (and are already demanding) changes to other parts. Just in the comments: why do we use dataset
instead of data-*
? Maybe because data-*
is not valid JS (much like class
) and because it's consistent with the DOM APIs? Why don't we change htmlFor
? Because for
is a reserved word and htmlFor
is in DOM APIs? Etc. etc. etc.
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.
@gaearon which is also the reason why React is the only library scoring bad on the CustomElements Everywhere interop test: https://custom-elements-everywhere.com/ And there's many issues asking to change it: https://github.com/facebook/react/issues/11347, https://github.com/facebook/react/issues/7249, https://github.com/facebook/react/issues/9230
This may be a non-issue, but is the similarity of React Fire to React Fiber going to be confusing for non-native English speakers? I've often thought that Newark Penn Station and New York Penn Station being on the same train line here in NYC was a particularly cruel joke on tourists.
If it's a real concern, you could call it React Flame and still keep the fire 🔥 emoji.
Would rewriting the event delegation open the door for fixing #1254; where some events degrade perf for the node they are attached to (which for React means the whole app).
Fixing #1254 in some form is definitely something I’d like to see.
Also, as a long shot, have you considered having synthetic dataSet props?
I don’t want to commit to something like this now because there’s a bigger surface. Richer interface for DOM (ariaSet
, dataSet
, classList
) makes sense but it’s not clear how much we want to invest in that in React DOM, versus a higher level library that gives you a richer DOM API. Since these changes are more cosmetic but have high surface area, I’d hold off with them.
React Blazing 🔥
@ryanflorence A bit offtop but it's kinda sad that no one (as far as I know of) had the idea to make a html/css/svg -> jsx transfomer in order to ease migrations to React with so many trivial changes to map HTML attrs to React props. So many man-hours wasted perfoming mostly find-and-replace :(
So excited for the new changes, you humans from Facebook are making history with this migration. 50k components will be migrated over to React Fire ? Your test suites must be so tight <3
is the similarity of React Fire to React Fiber going to be confusing for non-native English speakers?
maybe for hard-of-hearing folks, too (or those with other conditions affecting word discrimination)
Thanks for sharing @gaearon, that’s amasing!
I’d love to see a JSX 2.0 release solving the white space and new lines issue that occurs when we compile our code with Babel and Typescript.
There are different issues opened and I tried to contribute but got told off cause the React team needs to review everything around JSX.
This issue relates to the dom anyway, cause the way we translate jsx to js does not allow to what the w3c says.
This is the issue https://github.com/facebook/jsx/issues/19
My comment is at the very end.
I think, className
is ok. Let it be what it is. Don't add insult to injury.
Can someone please clarify how this affects existing handlers?
Stop reflecting input values in the value attribute
Will React still have controlled inputs with accurate event.target.value
updates in handlers, or does this only affect reading values from refs and DOM Nodes?
@nickmccurdy it affects what you see in the browser devtools
@tomdale React Ember 🔥
Nice! I'm waiting to see full list of changes in React 17. I believe it will be a new era of ReactJS. 🔥⚛️
@tomdale Hmm: Yarn, Fiber, Fabric; maybe another clothing related term could be used? 😆
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.
@gaearon which is also the reason why React is the only library scoring bad on the CustomElements Everywhere interop test: https://custom-elements-everywhere.com/
No, that's not the reason (custom and normal elements are completely separate code paths). The reason is that we already had the old behavior and didn't want to break backwards compat unless the new behavior was solid. I think it would make sense to tackle better custom element interop as part of this umbrella.
is the similarity of React Fire to React Fiber going to be confusing for non-native English speakers?
Both are internal codenames and don’t really carry any significance once the projects are completed. React Fire is just an effort to make React DOM better — and by the time it becomes production-ready it will be just React DOM.
Will React still have controlled inputs with accurate event.target.value updates in handlers, or does this only affect reading values from refs and DOM Nodes?
Yes, because event.target.value
is a property. We're talking about stopping updating attributes. Which no other popular libraries do (AFAIK) and which causes countless problems. It shouldn't affect your code unless you're relying on CSS selectors on the value (which is probably bad anyway).
Nice! I'm waiting to see full list of changes in React 17.
Note we're not committing to this being ready by 17. It might be 18. Or 19. 😅
For latest status, see an update from June 5th, 2019: https://github.com/facebook/react/issues/13525#issuecomment-499196939
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 (https://github.com/facebook/react/issues/11896). This was originally added in React 15.2.0 via https://github.com/facebook/react/pull/6406. It was very commonly requested because people's conceptual model of the DOM is that thevalue
they see in the DOM inspector should match thevalue
JSX attribute. But that's not how the DOM works. When you type into a field, the browser doesn't update thevalue
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: https://github.com/facebook/react/issues/7179, https://github.com/facebook/react/issues/8395, https://github.com/facebook/react/issues/7328, https://github.com/facebook/react/issues/7233, https://github.com/facebook/react/issues/11881, https://github.com/facebook/react/issues/7253, https://github.com/facebook/react/pull/9584, https://github.com/facebook/react/pull/9806, https://github.com/facebook/react/pull/9714, https://github.com/facebook/react/pull/11534, https://github.com/facebook/react/pull/11746, https://github.com/facebook/react/pull/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 (https://github.com/facebook/react/issues/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 (https://github.com/facebook/react/issues/8693, https://github.com/facebook/react/pull/8117, https://github.com/facebook/react/issues/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
toonInput
and don’t polyfill it for uncontrolled components (https://github.com/facebook/react/issues/9657). See the linked issue for a detailed plan. It has been confusing that React uses a different event name for what's known asinput
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 makeonInput
andonChange
work exactly how the DOM events do for uncontrolled components.Drastically simplify the event system (https://github.com/facebook/react/issues/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.
className
→class
(https://github.com/facebook/react/issues/4331, see also https://github.com/facebook/react/issues/13525#issuecomment-417818906 below). This has been proposed countless times. We're already allowing passingclass
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 processclassName
(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.