Repository navigation
React Fire: Modernizing React DOM #13525
Description
Activity
I love this. Reducing bundle size and the "class" prop are changes that will be very welcome.
Great work!
Reacted by Nate Radebaugh, Alex Parish, Joe Previte, Nando Sangenetto, Stephen Blum, cobr4kai, John Barker, Sercan Değirmenci, Constantine Antonakos, Danilo Setúbal and 151 moreReacted by pallyoung, JamesHighsmith, Yefim Blokh and Trésor Muco🙂
Reacted by Marvin Hagemeister, Nando Sangenetto, Stephen Blum, Jesse Hoffman, cobr4kai, Jeffrey, ibrmad, Zach Herries, Phips Peter, Haroen Viaene and 74 moreReacted by Haz, Kyle Kelley, Alex Parish, dan, hrmny, Marvin Hagemeister, Shahzeb K., Stephen Blum, Yeison Daza, cobr4kai and 54 moreReacted by Zouhir ⚡️, Iván, Kyle Villeneuve, Cyrus Venn Casada, Peter Kieltyka, David Schovanec, Sofiane Baddag, Andrey Gurtovoy, Teck, Tiaan and 6 moreReacted by Zouhir ⚡️, Iván, kelvin knighton, Cyrus Venn Casada, Peter Kieltyka, Oler, Marco Fugaro, Sofiane Baddag, Kazi Mehedi Hasan, Andrew M. and 15 moreReacted by Phuc Vo, resynth1943 and Fernando MolinaAttention form library authors! 🤣
Reacted by Sentayhu Mekoonn, Gian Marco Toso, Andrés, joelwatson, Samuel Chávez, Dave Lunny, Botond Veress, Sam Clark, Pascal Vos, Markoz Peña and 10 moreReacted by Jeff Gnatek, David Khourshid, Peter Mikitsh, Nathan White, Yuri Pereira Constante, Stephen Blum, Christopher Miller, Jeffrey, Gosha Spark, wtfdaemon and 61 moreGreat!
Reacted by Jeffrey, Wonmin Jeon, Oler and Initial MReacted by Stephen Blum and OlerclassName → class is fantastic
What about all the others? Seems weird to still be doing
clipPath,htmlFor,tabIndex, etc.Reacted by Siddharth Kshetrapal, Kent C. Dodds, Shahzeb K., Nando Sangenetto, Nataliya Karatkova, Kyle Holmberg, Mohamed Sobhy, Stephen Blum, Andrew Lisowski, Jón Trausti and 341 moreReacted by Aria Buckles, John Sanders, Oler, Nikki Strømsnes, Alex Hansen, Luca Steeb, Nate Wienert, Luciano Battagliero, James Moore, Qin Junwen and 10 moreReacted by Zhaohan Weng and Laura bunsReacted by Tanner Linsley, Erik Rasmussen, Remy Rylan, Daniel O’Connor, Stephen Blum, Scott Tolinski, Gustavo de Paula, Christopher Miller, Derek Lindahl, wtfdaemon and 23 moreReacted by Florian Wendelborn, Bastien and Mayank ChauhanReacted by Ross Warren, Kevin Eaton, Luciano Battagliero, nahum zsilva, Andy Fleming, Aria Buckles, Yadullah Duman, Farkhad Khatamov, Jose Santos, Oler and 13 moreAdopting
classis a major breakthrough in making the library more friendly for beginners. Congratulations.Reacted by Elyse Holladay, Alex Parish, Kyle Holmberg, Stephen Blum, Juan David Castro, Michal Hantl, Dawson Botsford, Andy Fleming, Aria Buckles, David Young and 71 moreReacted by John Sanders, graemeenglish, Arend van Beelen jr., Alex Hansen, Roger Kondrat, Luciano Battagliero, James Moore, Michael Martinez, Bastien, Marco Vanali and 16 moreThis is awesome. I'm so curious how the move to
classis actually going work with props.Seems like
({ class }) => <div class={class} />would initially present a reserved keyword problem?Reacted by Siddharth Kshetrapal, Ahmed El Gabri, Yuri Pereira Constante, Stephen Blum, Jefferson Ribeiro, Ruslan @doasync, Leonardo Dino, Oleksii, Waseem Dahman, Adam Berro and 173 moreReacted by Ruslan @doasync, Matt Popovich, Juan David Castro, Eric Bower, Dawson Botsford, Oler, Alex Hansen, omaksi, Vijay Britto, SHEN Lin and 4 moreReacted by Ryan, Hristo Kanchev, Jacob M-G Evans, Avery Bross, David Cook and JasonReacted by MuYunyun, McKayla はな and Fernando MolinaThis is fantastic news, thanks @gaearon!
Reacted by Stephen Blum, Wonmin Jeon and Anton HoncharukI love every of these points, except the
classNamechange. 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 namedclassName, notclass. So why would it be namedclassin React?Reacted by mindhalt, Ben Hollander, Stephen Blum, zlydenko, Sean M. Vieira, Charley DAVID, Gustavo de Paula, Christopher Miller, Maciej Kozik, Tushar Singh and 534 moreReacted by Halid Cisse, Kazi Mehedi Hasan, Joe, David Gilbertson, Igor Bari, Aydar Zartdinov, Jonas, André Marques, Rodrigo, Luke and 1 moreReacted by Boaz Blake, Bonnie Milián, Mauro Pineda, Can Rau, Aggrey1987 and GandharvReacted by wtfdaemon, Alex Permiakov, Tom Lingham, nahum zsilva, Oler, Alex Hansen, Shaun Russell, omaksi, Qin Junwen, Bonnie Milián and 9 moreReacted by Eduardo Rabelo, Willow Chargin, Oler, Martin Novák, Alex Hansen, Roger Kondrat, Julien Meichelbeck, Shaun Russell, Abi Hafshin Alfarouq, Petja Touru and 29 moreReacted by MuYunyun and Aggrey1987Fantastic! Do you have a goal for bundle size reduction?
Reacted by Stephen Blum, Helder S Ribeiro, Jaume Tarradas Llort, Markoz Peña, Scott Sullivan, Lorenzo Stanco and Aggrey1987👏
Reacted by Stephen Blum and Markoz PeñaWhat 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
formaybe).Reacted by Stephen Blum, Jordan Addison, _, Matt Popovich, Giu Magnani, Juan David Castro, Luciano Battagliero, Andy Fleming, Zack Harley, Ajit and 27 moreReacted by Roger Kondrat, Jiri Spac, rauno, Jarrod Payne, Xandor Schiefer and JohnReacted by Luciano Battagliero, Saad Quadri, Tiago Almeida, Tanguy Krotoff, João Vieira, Max and Brian KimI 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!
Reacted by Alex Parish, dan, Kent C. Dodds, Stephen Blum, Jason Quense, Haroen Viaene, Daniel Waltrip, Jason Miller, Joe Alden, Fermin Blanco and 39 more202 remaining items
Load more actionsWill "React Flare" come as a part of the default React package or need an additional install considering the amount of API's it will ship with?
We'd like to avoid unused code getting bundled. So the current intention is for it to be opt-in per API. Maybe separate entry points in a single package.
Reacted by danielsdesk and JamesReacted by Rahul , Jacob M-G Evans and Hossein MoradiWill this mouse and touch event system leverage PointerEvent? I did not see a mention of this web standard in the previous update, so I just wanted to bring it to your attention.
Pointer events are DOM events that are fired for a pointing device. They are designed to create a single DOM event model to handle pointing input devices such as a mouse, pen/stylus or touch (such as one or more fingers). The pointer is a hardware-agnostic device that can target a specific set of screen coordinates. Having a single event model for pointers can simplify creating Web sites and applications and provide a good user experience regardless of the user's hardware.
And here is a direct link to the current browser compatibility.
@jonathantneal Yes, the new system heavily use Pointer Events – with fallbacks to Mouse/Touch events when there's no support for Pointer Events.
Reacted by Hunter m. and Moritz MahringerReacted by AdrianI am concerned that #11347 was not addressed in this issue. React flunks https://custom-elements-everywhere.com.
Reacted by Alan Dávalos, Romulo Cintra, Adam Palaniuk, bySabi Files, Sebastian Busch, Fabian, Adam Ochayon and Anselm Marie- Reacted by Matija Folnovic, Nikolai Røed Kristiansen, cybai (Haku), Trotyl Yu, Zain Fathoni, morbidick, Romulo Cintra, Takahiro Matsuda, Praveen Sampath, Sebastian Kurfürst and 6 more
In this update: #13525 (comment) @gaearon mentions:
However, as we started removing parts of the event system that we thought were unnecessary or outdated, we discovered many edge cases where it was being very helpful and prevented bugs — even in modern browsers.
I was curious if a list of these edge cases are documented anywhere?
Reacted by Christian Kaindl, Eddie, Lorenzo Stanco, mrsimb and Moritz MahringerReacted by Karl Horky and Christian KaindlReacted by Brian Kim, Christian Kaindl and cybai (Haku)- addedReact Core TeamOpened by a member of the React Core TeamOpened by a member of the React Core Team
on Jan 8, 2020 @gaearon now that Flare has gone out (SCNR), is there an updated plan (regarding the June 5th, 2019 update) how to proceed?
And like @trusktr, I also would like to get #11347 addressed here.
Reacted by gut4, Ivan Dmitriev, Jafar Rezaei and Brandon LillyCould be split polyfills into another bundle especially the one not relevant to major evergreen browsers.
Hey all, it's been a while and we've tried some of these things on and off.
Let me give an update on each:
- Stop reflecting input values in the
valueattribute (Stop syncing value attribute for controlled inputs #11896). This was originally added in React 15.2.0 via Properly set value and defaultValue for input and textarea #6406. It was very commonly requested because people's conceptual model of the DOM is that thevaluethey see in the DOM inspector should match thevalueJSX attribute. But that's not how the DOM works. When you type into a field, the browser doesn't update thevalueattribute. 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: Form submit button has empty values in 15.2.0 #7179, Firefox validation triggers on input component render #8395, IE 11 and Edge no longer prompt to remember password on controlled form #7328, Date input with defaultValue regression in 15.2 #7233, backspace fails to clear values on input type='email' #11881, Bug: Backspace in input type="number" behaves badly in Blink #7253, Remove loose check on non-number controlled inputs. Fix trailing dot issue. #9584, Inputs should not mutate value on type conversion (when they stringify to the same thing) #9806, [#9712] fix <input type="number" /> value '.98' should not be equal to '0.98'. #9714, Use defaultValue instead of setAttribute('value') #11534, Use the same value synchronization function on number blur #11746, Do not assign node.value on input creation if no change will occur #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.
We still want to do this, but we've decided to "reserve" React 17 to have minimal possible breaking changes so that it can focus on the next item on this list. So this change will wait until React 18.
- Attach events at the React root rather than the document (Attach event per react container root, rather than on 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
stopPropagationinteracting with non-React code or across React roots (Native event.stopPropagation outside of React root cuts out React events #8693, Attach event listeners at the root of the tree instead of document #8117, Event listener attached todocumentwill still be called after callingevent.stopPropagation()#12518). We will also want to attach events eagerly to every root so that we can do less runtime checks during updates.
We're doing this in React 17. This turned out to be a huge chunk of work but thankfully it's finished.
- Migrate from
onChangetoonInputand don’t polyfill it for uncontrolled components ([RFC] onChange -> onInput, and don't polyfill onInput 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 asinputevent 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 makeonInputandonChangework exactly how the DOM events do for uncontrolled components.
We'll likely come back to this but it's unclear how much churn is worth doing here. So this is still TBD.
- Drastically simplify the event system (Play Nicely with The DOM Event System (because it's legacy anyway) #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.
We've tried this early in 2019, and a truly minimal event system did not work out very well in our internal testing. There was quite a bit of cross-browser normalization React is doing that is still useful for people with older browsers, or in more niche areas like rich text input editors using
contentEditable. That said, as a part of our work on attaching events to roots, we've removed a lot of abstraction from the event system so it's easier to understand and improve in the future. As a part of React 17, we're removing "event pooling" which has caused a lot of confusion, and we're also no longer bubbling theonScrollevent. We will likely follow with stopping bubbling of media events in React 18, bringing React behavior closer to the browser one. We did save some bytes with the new event system, but they were taken by new features we're working on, so it won't lead to a bundle size decrease overall.className→class(Why are attribute names "class" and "for" discouraged? #4331, see also React Fire: Modernizing React DOM #13525 (comment) below). This has been proposed countless times. We're already allowing passingclassdown 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.
This was the most controversial part of the proposal. Since then, we released Hooks, which encourage writing function components. In function components, we generally suggest using destructuring for props, but you can't write
{ class, ... }because it would be a syntax error. So overall it's not clear that this is ergonomic enough to actually follow through with. I think it's plausible we'll revisit this in the future, or at least makeclassnot warn and let people do what they want. But for now, we'll shelving this idea.Reacted by Philipp Spiess, Bruno Carneiro, Masafumi Koba, Charles Pick, Michaël De Boey, Mike, Hyeseong Kim, Gabriel, Constantine Antonakos, Ivan Kleshnin and 27 moreReacted by Jacob M-G Evans, Param Aggarwal, Patrick Coffey, Nikos Benakis and Veniamin KrolReacted by Mathspy, Jacob M-G Evans, Param Aggarwal, Patrick Coffey, Nikos Benakis, Karl Horky, Nikolai Røed Kristiansen, Veniamin Krol and Ezequiel GarridoReacted by Patrick Coffey, Veniamin Krol and Peter HozákReacted by Brian Kim and Alex Kondratiuk- Stop reflecting input values in the
Hi, it's a great article!
Just wanted to know if there is any plan in pipeline to reduce React-DOM prod size? For mobile applications, it is still an overhead as the browser will be parsing 100+ KB of React-DOM JS and then other modules. Then app-specific JS.
For content-rich pages, it is causing greater Blocking and greater TTI.Any Idea when can we see such changes?
Reacted by Ivan Kleshnin, Pablo Sáez, Gabriel, Chiawen Chen, Truong Fiu and Tommy Troy Lin@morevolk-latei In your measurements, how much time is spent parsing 100 KB of ReactDOM?
Reacted by Alexander Kachkaev, trevyn and gerdazkReacted by Peter Hozák- added a commit that references this issue
on May 26, 2022
For latest status, see an update from June 5th, 2019: #13525 (comment)
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
valueattribute (Stop syncing value attribute for controlled inputs #11896). This was originally added in React 15.2.0 via Properly set value and defaultValue for input and textarea #6406. It was very commonly requested because people's conceptual model of the DOM is that thevaluethey see in the DOM inspector should match thevalueJSX attribute. But that's not how the DOM works. When you type into a field, the browser doesn't update thevalueattribute. 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: Form submit button has empty values in 15.2.0 #7179, Firefox validation triggers on input component render #8395, IE 11 and Edge no longer prompt to remember password on controlled form #7328, Date input with defaultValue regression in 15.2 #7233, backspace fails to clear values on input type='email' #11881, Bug: Backspace in input type="number" behaves badly in Blink #7253, Remove loose check on non-number controlled inputs. Fix trailing dot issue. #9584, Inputs should not mutate value on type conversion (when they stringify to the same thing) #9806, [#9712] fix <input type="number" /> value '.98' should not be equal to '0.98'. #9714, Use defaultValue instead of setAttribute('value') #11534, Use the same value synchronization function on number blur #11746, Do not assign node.value on input creation if no change will occur #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 (Attach event per react container root, rather than on 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
stopPropagationinteracting with non-React code or across React roots (Native event.stopPropagation outside of React root cuts out React events #8693, Attach event listeners at the root of the tree instead of document #8117, Event listener attached todocumentwill still be called after callingevent.stopPropagation()#12518). We will also want to attach events eagerly to every root so that we can do less runtime checks during updates.Migrate from
onChangetoonInputand don’t polyfill it for uncontrolled components ([RFC] onChange -> onInput, and don't polyfill onInput 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 asinputevent 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 makeonInputandonChangework exactly how the DOM events do for uncontrolled components.Drastically simplify the event system (Play Nicely with The DOM Event System (because it's legacy anyway) #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(Why are attribute names "class" and "for" discouraged? #4331, see also React Fire: Modernizing React DOM #13525 (comment) below). This has been proposed countless times. We're already allowing passingclassdown 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.