Calling ReactDOM.render() many many times is slow
Do you want to request a feature or report a bug?
Bug (?)
What is the current behavior?
Calling ReactDOM.render()
many many times seems to have meaningfully worse performance than rendering many elements within a single React root.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
// Edited to add dev/production builds
- React 16 (development): https://jsfiddle.net/Luktwrdm/431/
- React 16 (production): https://jsfiddle.net/Luktwrdm/433/
- React 15 (development): https://jsfiddle.net/Luktwrdm/432/
(These examples are obviously pretty contrived)
What is the expected behavior?
In a perfect world there wouldn't be such a large performance discrepancy between these two approaches.
For context, I'm working with a frontend plugin framework and trying not to expose React (which should ideally be an implementation detail) as part of the plugin interface.
An interface like this requires both the host and the plugin to be implemented with React and to share the same instance of React... but is fast and convenient when they do:
render(props: T): JSX.Element;
An interface like this treats React as an implementation detail, but is less convenient and (more importantly) incurs the above performance problem:
render(props:T, element: HTMLElement): void;
unmount(element: HTMLElement): void;
I imagine this use case isn't a high priority for React/ReactDOM, but I'd love to understand a bit better what it is that really causes the performance difference and whether it's likely to ever change.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
- Seems to impact both React 15 & 16 similarly
- I don't think it's browser dependent
推荐相似问题
Styles via className not reflected in print
Conditional Rendering Such as JSX-Control-Statements
Ref callback is not called before componentDidMount
Allow to specify displayName for createContext() providers and consumers
Size prop is not attached to the DOM
react-test-renderer.create does not work properly with forwardRef components
<textarea autoFocus/> seems to leak DOM nodes
It's possible to do unlimited recursive rendering with an HOC
Cloning the child of a context Consumer produces confusing warning and error
Unexpected behavior when use React.forwardRef with React.createContext
基本信息
- 回复数:329
- 讨论框架:react
- 原始内容:查看信息
- 最后更新于:2020-10-23
8条回答
I haven't looked yet but my first question is whether the difference is significant in production mode. The fiddles you're showing are running in development mode.
From what I've seen the difference is similar in production mode, you just start to really see it at higher element counts.
I've played around a little trying to understand how different usage patters impact it. Frequency of updates which require a DOM change, for example, or updates vs lots of mounting & unmounting components. So far I haven't really isolated anything particularly useful.
Not sure if I understand React under the hood correctly, but let's think about it.
When you call
ReactDOM.render
you need to destroy/replace contents of the target node.Meanwhile, if you use a single root then Virtual DOM will take care of DOM operations.
We know that DOM mutations are pretty (very?) expensive.
So.. I wouldn't be surprised.
Is this actually true? My understanding is that that's actually not the case. With the repro examples what's actually being rendered is the same in either case way so the number of DOM mutations don't have to be wildly different.
That said, I can understand some overhead for each root and how many roots might make batching / scheduling worse -- I just don't know enough about the guts of React's implementation to fully explain it.
There shouldn't be a difference in DOM mutations.
I fixed the biggest difference that jumped out at me in #13335.
There's some other differences in DEV (e.g. in React 15 we don't emit perf measurements by default, but in React 16 we do, and they have a fixed cost). But in production it should get better in the next patch release that includes #13335 (presumably 16.4.3 or 16.5.0).
Note in general though we don't recommend using many roots when it's avoidable. It's supported of course but it makes React's job more difficult. As far as I can see, if you use
createPortal()
in therender
method instead of having manyReactDOM.render()
calls, the performance significantly improves even in the version of React that's currently published. In general, I think your goal of abstracting away React can make it more difficult for you to benefit from React-specific optimizations like time slicing in the future — so that's another thing to consider.Thanks for the repro case!
@gaearon
If so, why in the React tests we use calling the
ReactNoop.render
orReactDOM.render
many times, should we use other way to test such assetState
in the root component?The difference is not significant enough to matter in tests.