How to do perf analysis in React 16 - javascript

The React docs state that react-addons-perf does not work with React 16, but that Chrome's built-in tools provide equivalent functionality. I have not found this to be the case.
For instance, let's say that I've made the classic mistake of not including a proper key on a list of elements (demo code is on GitHub):
render() {
const items = this.state.items.map((item, index) => <ListItem key={index} name={item.name} />)
return <div>
<button onClick={this.addItem}>Add item</button>
<ul>{items}</ul>
</div>;
}
The key={index} issue will cause every ListItem to re-render when I add an item to the list. Using React 15 perf tools, I can easily discover this:
Once I apply the fix, I can see that the problem has gone away:
However, with React 16 and the Chrome dev tools, I'm not sure how to get equivalent information. (Demo code with React 16.) Here's the profiling results from when the bug was present:
And here are the profiling results from when the bug was absent:
I don't know how I'd look at these profiling results and get the same information that react-addons-perf was providing. And, my results don't look anything like the React docs:
I'm on Chrome 63.0.3239.108. Is the React team on a different version of Chrome, or do you need to enable special flags to make this work?
Potentially related: How can I measure wasted renders in React 16?.
tl;dr
How can I get useful information from the Chrome profiler?
Why do my profiler sessions look different from the React docs?
Update
Thanks Rob. M! Turns out I was looking in the wrong tab in the dev tools. When I open the User Timing section, I see the difference between the good and the bad:
Bad, with many ListItem updates:
Good, with one ListItem update:

React events are logged under the "User Timing" label - if you expand that label instead of "Main" you should find the component performance data you are looking for.

Related

React Native custom cross-platform dropdown select element

I have searched quite thoroughly with not much luck. I am having trouble implementing a simple custom dropdown select element for my application.
I am hoping for a cross-platform (iOS & Android) solution like the simple custom JS dropdown featured here https://www.w3schools.com/howto/howto_custom_select.asp
Is this something I should just be creating myself with react native views and some state? I'm still trying to get a feel of what solutions are unrealistic. Could anyone give me an example from a professional project?
I am trying to avoid using the native Picker from here since I cannot style them to match my designer's needs. https://github.com/react-native-community/react-native-picker
I tried this package but it seems to be having trouble with the latest versions and I need something reliable. https://github.com/sohobloo/react-native-modal-dropdown
I was also looking at this package as it seems to be nearly perfect for my needs but again it has not been updated in 2 years https://github.com/n4kz/react-native-material-dropdown
My recommendation would be to go with something out there, as I will generally make implementation quicker for you, although as you say there might be a trade off in styling.
I've recently been in the situation a few times where I've really had to look hard for good, maintained components for React Native, but they certainly exist.
I've been successfully using react-native-paper recently. You could consider their accordion drop down list but may not suit in styling as it follows material UI guidelines.
https://callstack.github.io/react-native-paper/
I used this in my project and it works perfectly:
https://github.com/mrlaessig/react-native-autocomplete-input
you can just use the onFocus prop to make it dropdown with a click instead of when the user firsts typing and onEndEditing prop to hide it when the user selects an option.
here's my full implementation for reference, I had to go into the code a bit to check for these props since the documentation didn't include it.
<Autocomplete
data={filterNames()}
placeholder={'Who are you here to see?'}
onChangeText={item => setTextAndShowResults(item)}
onFocus={() => setHideResults(false)}
onEndEditing={() => setHideResults(true)}
defaultValue={getDefaultValue()}
keyExtractor={(item, i) => {
return item.id;
}}
returnKeyType={'next'}
listStyle={styles.listStyle}
inputContainerStyle={styles.borderWidth0}
style={styles.autoCompleteButtonStyle}
hideResults={hideResults}
renderItem={({ item, i }) => (
<TouchableOpacity onPress={() => setTextAndHideResults(item)}>
<Text style={styles.listTextStyle}>{item.name}</Text>
</TouchableOpacity>
)}
/>

React DevTools show components as "Loading..."

I need to debug some props that are being used in my React application. However, if I try to inspect certain components, they appear as "Loading..." instead of the normal props list:
However, I know that these components have already rendered as I can see them in the DOM. Also, it stays like this perpetually, so it obviously isn't actually loading anything.
I'm using React DevTools 4.2.1-3816ae7c3 and React 16.12.0
I was having the same issue with Ubuntu and after reinstalling the extension. Close the chrome app and reopen it, then it solved it for me.

Violation Long running JavaScript task took xx ms

Recently, I got this kind of warning, and this is my first time getting it:
[Violation] Long running JavaScript task took 234ms
[Violation] Forced reflow while executing JavaScript took 45ms
I'm working on a group project and I have no idea where this is coming from. This never happened before. Suddenly, it appeared when someone else got involved in the project. How do I find what file/function causes this warning? I've been looking for the answer, but mostly about the solution on how to solve it. I can't solve it if I can't even find the source of the problem.
In this case, the warning appears only on Chrome. I tried to use Edge, but I didn't get any similar warnings, and I haven't tested it on Firefox yet.
I even get the error from jquery.min.js:
[Violation] Handler took 231ms of runtime (50ms allowed) jquery.min.js:2
Update: Chrome 58+ hid these and other debug messages by default. To display them click the arrow next to 'Info' and select 'Verbose'.
Chrome 57 turned on 'hide violations' by default. To turn them back on you need to enable filters and uncheck the 'hide violations' box.
suddenly it appears when someone else involved in the project
I think it's more likely you updated to Chrome 56. This warning is a wonderful new feature, in my opinion, please only turn it off if you're desperate and your assessor will take marks away from you. The underlying problems are there in the other browsers but the browsers just aren't telling you there's a problem. The Chromium ticket is here but there isn't really any interesting discussion on it.
These messages are warnings instead of errors because it's not really going to cause major problems. It may cause frames to get dropped or otherwise cause a less smooth experience.
They're worth investigating and fixing to improve the quality of your application however. The way to do this is by paying attention to what circumstances the messages appear, and doing performance testing to narrow down where the issue is occurring. The simplest way to start performance testing is to insert some code like this:
function someMethodIThinkMightBeSlow() {
const startTime = performance.now();
// Do the normal stuff for this function
const duration = performance.now() - startTime;
console.log(`someMethodIThinkMightBeSlow took ${duration}ms`);
}
If you want to get more advanced, you could also use Chrome's profiler, or make use of a benchmarking library like this one.
Once you've found some code that's taking a long time (50ms is Chrome's threshold), you have a couple of options:
Cut out some/all of that task that may be unnecessary
Figure out how to do the same task faster
Divide the code into multiple asynchronous steps
(1) and (2) may be difficult or impossible, but it's sometimes really easy and should be your first attempts. If needed, it should always be possible to do (3). To do this you will use something like:
setTimeout(functionToRunVerySoonButNotNow);
or
// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(functionToRunVerySoonButNotNow);
You can read more about the asynchronous nature of JavaScript here.
These are just warnings as everyone mentioned. However, if you're keen on resolving these (which you should), then you need to identify what is causing the warning first. There's no one reason due to which you can get force reflow warning.
Someone has created a list for some possible options. You can follow the discussion for more information.
Here's the gist of the possible reasons:
What forces layout / reflow
All of the below properties or methods, when requested/called in
JavaScript, will trigger the browser to synchronously calculate the
style and layout*. This is also called reflow or layout
thrashing,
and is common performance bottleneck.
Element
Box metrics
elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent
elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight
elem.getClientRects(), elem.getBoundingClientRect()
Scroll stuff
elem.scrollBy(), elem.scrollTo()
elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
elem.scrollWidth, elem.scrollHeight
elem.scrollLeft, elem.scrollTop also, setting them
Focus
elem.focus() can trigger a double forced layout (source)
Also…
elem.computedRole, elem.computedName
elem.innerText (source)
getComputedStyle
window.getComputedStyle() will typically force style recalc
(source)
window.getComputedStyle() will force layout, as well, if any of the
following is true:
The element is in a shadow tree
There are media queries (viewport-related ones). Specifically, one of the following:
(source) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio, max-aspect-ratio
device-pixel-ratio, resolution, orientation
The property requested is one of the following: (source)
height, width * top, right, bottom, left * margin [-top, -right, -bottom, -left, or shorthand] only if the
margin is fixed. * padding [-top, -right, -bottom, -left,
or shorthand] only if the padding is fixed. * transform,
transform-origin, perspective-origin * translate, rotate,
scale * webkit-filter, backdrop-filter * motion-path,
motion-offset, motion-rotation * x, y, rx, ry
window
window.scrollX, window.scrollY
window.innerHeight, window.innerWidth
window.getMatchedCSSRules() only forces style
Forms
inputElem.focus()
inputElem.select(), textareaElem.select() (source)
Mouse events
mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY
(source)
document
doc.scrollingElement only forces style
Range
range.getClientRects(), range.getBoundingClientRect()
SVG
Quite a lot; haven't made an exhaustive list , but Tony Gentilcore's 2011 Layout Triggering
List
pointed to a few.
contenteditable
Lots & lots of stuff, …including copying an image to clipboard (source)
Check more here.
Also, here's Chromium source code from the original issue and a discussion about a performance API for the warnings.
Edit: There's also an article on how to minimize layout reflow on PageSpeed Insight by Google. It explains what browser reflow is:
Reflow is the name of the web browser process for re-calculating the
positions and geometries of elements in the document, for the purpose
of re-rendering part or all of the document. Because reflow is a
user-blocking operation in the browser, it is useful for developers to
understand how to improve reflow time and also to understand the
effects of various document properties (DOM depth, CSS rule
efficiency, different types of style changes) on reflow time.
Sometimes reflowing a single element in the document may require
reflowing its parent elements and also any elements which follow it.
In addition, it explains how to minimize it:
Reduce unnecessary DOM depth. Changes at one level in the DOM tree
can cause changes at every level of the tree - all the way up to the
root, and all the way down into the children of the modified node.
This leads to more time being spent performing reflow.
Minimize CSS rules, and remove unused CSS rules.
If you make complex rendering changes such as animations, do so out of the flow. Use position-absolute or position-fixed to accomplish
this.
Avoid unnecessary complex CSS selectors - descendant selectors in
particular - which require more CPU power to do selector matching.
A couple of ideas:
Remove half of your code (maybe via commenting it out).
Is the problem still there? Great, you've narrowed down the possibilities! Repeat.
Is the problem not there? Ok, look at the half you commented out!
Are you using any version control system (eg, Git)? If so, git checkout some of your more recent commits. When was the problem introduced? Look at the commit to see exactly what code changed when the problem first arrived.
I found the root of this message in my code, which searched and hid or showed nodes (offline). This was my code:
search.addEventListener('keyup', function() {
for (const node of nodes)
if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
node.classList.remove('hidden');
else
node.classList.add('hidden');
});
The performance tab (profiler) shows the event taking about 60 ms:
Now:
search.addEventListener('keyup', function() {
const nodesToHide = [];
const nodesToShow = [];
for (const node of nodes)
if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
nodesToShow.push(node);
else
nodesToHide.push(node);
nodesToHide.forEach(node => node.classList.add('hidden'));
nodesToShow.forEach(node => node.classList.remove('hidden'));
});
The performance tab (profiler) now shows the event taking about 1 ms:
And I feel that the search works faster now (229 nodes).
In order to identify the source of the problem, run your application, and record it in Chrome's Performance tab.
There you can check various functions that took a long time to run. In my case, the one that correlated with warnings in console was from a file which was loaded by the AdBlock extension, but this could be something else in your case.
Check these files and try to identify if this is some extension's code or yours. (If it is yours, then you have found the source of your problem.)
Look in the Chrome console under the Network tab and find the scripts which take the longest to load.
In my case there were a set of Angular add on scripts that I had included but not yet used in the app :
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-utils/0.1.1/angular-ui-utils.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-aria.min.js"></script>
These were the only JavaScript files that took longer to load than the time that the "Long Running Task" error specified.
All of these files run on my other websites with no errors generated but I was getting this "Long Running Task" error on a new web app that barely had any functionality. The error stopped immediately upon removing.
My best guess is that these Angular add ons were looking recursively into increasingly deep sections of the DOM for their start tags - finding none, they had to traverse the entire DOM before exiting, which took longer than Chrome expects - thus the warning.
I found a solution in Apache Cordova source code.
They implement like this:
var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };
Simple implementation, but smart way.
Over the Android 4.4, use Promise.
For older browsers, use setTimeout()
Usage:
nextTick(function() {
// your code
});
After inserting this trick code, all warning messages are gone.
Adding my insights here as this thread was the "go to" stackoverflow question on the topic.
My problem was in a Material-UI app (early stages)
placement of custom Theme provider was the cause
when I did some calculations forcing rendering of the page
(one component, "display results", depends on what is set in others, "input sections").
Everything was fine until I updated the "state" that forces the "results component" to rerender. The main issue here was that I had a material-ui theme (https://material-ui.com/customization/theming/#a-note-on-performance) in the same renderer (App.js / return.. ) as the "results component", SummaryAppBarPure
Solution was to lift the ThemeProvider one level up (Index.js), and wrapping the App component here, thus not forcing the ThemeProvider to recalculate and draw / layout / reflow.
before
in App.js:
return (
<>
<MyThemeProvider>
<Container className={classes.appMaxWidth}>
<SummaryAppBarPure
//...
in index.js
ReactDOM.render(
<React.StrictMode>
<App />
//...
after
in App.js:
return (
<>
{/* move theme to index. made reflow problem go away */}
{/* <MyThemeProvider> */}
<Container className={classes.appMaxWidth}>
<SummaryAppBarPure
//...
in index.js
ReactDOM.render(
<React.StrictMode>
<MyThemeProvider>
<App />
//...
This was added in the Chrome 56 beta, even though it isn't on this changelog from the Chromium Blog: Chrome 56 Beta: “Not Secure” warning, Web Bluetooth, and CSS position: sticky
You can hide this in the filter bar of the console with the Hide violations checkbox.
This is violation error from Google Chrome that shows when the Verbose logging level is enabled.
Example of error message:
Explanation:
Reflow is the name of the web browser process for re-calculating the positions and geometries of elements in the document, for the purpose of re-rendering part or all of the document. Because reflow is a user-blocking operation in the browser, it is useful for developers to understand how to improve reflow time and also to understand the effects of various document properties (DOM depth, CSS rule efficiency, different types of style changes) on reflow time. Sometimes reflowing a single element in the document may require reflowing its parent elements and also any elements which follow it.
Original article: Minimizing browser reflow by Lindsey Simon, UX Developer, posted on developers.google.com.
And this is the link Google Chrome gives you in the Performance profiler, on the layout profiles (the mauve regions), for more info on the warning.
If you're using Chrome Canary (or Beta), just check the 'Hide Violations' option.
For what it’s worth, here are my 2¢ when I encountered the
[Violation] Forced reflow while executing JavaScript took <N>ms
warning. The page in question is generated from user content, so I don’t really have much influence over the size of the DOM. In my case, the problem is a table of two columns with potentially hundreds, even thousands of rows. (No on-demand row loading implemented yet, sorry!)
Using jQuery, on keydown the page selects a set of rows and toggles their visibility. I noticed that using toggle() on that set triggers the warning more readily than using hide() & show() explicitly.
For more details on this particular performance scenario, see also this article.
The answer is that it's a feature in newer Chrome browsers where it alerts you if the web page causes excessive browser reflows while executing JS. Please refer to
Forced reflow often happens when you have a function called multiple times before the end of execution.
For example, you may have the problem on a smartphone, but not on a classic browser.
I suggest using a setTimeout to solve the problem.
This isn't very important, but I repeat, the problem arises when you call a function several times, and not when the function takes more than 50 ms. I think you are mistaken in your answers.
Turn off 1-by-1 calls and reload the code to see if it still produces the error.
If a second script causes the error, use a setTimeOut based on the duration of the violation.
This is not an error just simple a message. To execute this message change
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> (example)
to
<!DOCTYPE html>(the Firefox source expect this)
The message was shown in Google Chrome 74 and Opera 60 . After changing it was clear, 0 verbose.
A solution approach

Components with large datasets runs slow on IE11/Edge only

Consider the code below. <GridBody Rows={rows} /> and imagine that rows.length would amount to any value 2000 or more with each array has about 8 columns in this example. I use a more expanded version of this code to render a part of a table that has been bottle necking my web application.
var GridBody = React.createClass({
render: function () {
return <tbody>
{this.props.Rows.map((row, rowKey) => {
return this.renderRow(row, rowKey);
})}
</tbody>;
},
renderRow: function (row, rowKey) {
return <tr key={rowKey}>
{row.map((col, colKey) => {
return this.renderColumn(col, colKey);
})}
</tr>;
},
renderColumn: function (col, colKey) {
return <td key={colKey} dangerouslySetInnerHTML={{ __html: col } }></td>;
}
});
Now onto the actual problem. It would seem that computation (even with my own code) seems to be suprisingly fast and even ReactJS's work with the virtualDOM has no issues.
But then there are these two events in reactJS.
componentWillUpdate up until where everything is still okay.
And then there is componentDidUpdate which seems to be fine and all on chrome.
The problem
But then there is IE11/Edge with about 4-6 SECONDS slower than any other browser and with the F12-Inspector this seems to be p to 8 SECONDS slower than Chrome.
The steps I have done to try and fix this issue:
Strip unnecessary code.
Shave off a handful of milliseconds in computation time.
Split the grid in loose components so that the virtualDOM doesn't try
to update the entire component at once.
Attempt to concaternate everything as a string and allow react to
only set innerhtml once. This actually seems to be a bug in IE here a
large string takes about 25-30 seconds on IE11. And only 30 ms on
chrome.
I have not found a proper solution yet. The actions I have done above seemed to make things less bad in IE but the problem still persists that a "modern" or "recent" browser is still 3-4 seconds slower.
Even worse, this seems to nearly freeze the entire browser and it's rendering.
tl;dr How to improve overal performance in IE and if possible other browsers?
I apologize if my question is unclear, I am burned out on this matter.
edit: Specifically DOM access is slow on IE as set innerHTML gets called more than 10.000 times. Can this be prevented in ReactJS?
Things to try improve IE performance:
check you are running in production mode (which removes things like prop validation) and make Webpack / Babel optimisations where applicable
Render the page server side so IE has no issues (if you can support SS rendering in your setup)
Make sure render isnt called alot of times, tools like this are helpful: https://github.com/garbles/why-did-you-update
Any reason why you are using dangerouslySetInnerHTML? If you take out the dangerouslySetInnerHTML does it speed things up dramatically?
Why not just automatically generate the rows and cols based on a array of objects (or multidimensional array passed), im pretty sure then React will make less DOM interaction this way (makes use of the VDOM). The <tr> and <td>'s will be virtual dom nodes.
Use something like https://github.com/bvaughn/react-virtualized to efficiently render large lists
Shot in the dark: try not rendering, or not displaying, until everything is completely done.
make the table element display:none until it's done
render it offscreen
in a tiny DIV with hidden overflow
or even output to a giant HTML string and then insert that into the DOM upon completion.
In addition to #Marty's excellent points, run a dynaTrace session to pinpoint the problematic code. It should give you a better insight into where the bottleneck is. It's results are often more useful than the IE developer tools.
Disclaimer - I am not linked with the dynaTrace team in any way.

How to measure the time to bind/render a view or directive?

I have a view that contains two directives. I want to know how much time it takes to update/bind/display each part (the view, the first directive and the other directive).
I'm looking for an end-to-end duration, including the time spent in JS and the actual browser rendering time;
I know Batarang helps measuring watches but here I'm looking at the bigger picture.
In Chrome, I started a CPU Profile and hit refresh. In the Flame Chart view, I see that scope.$digest took 91 ms. But that's for the whole view including the directives right? And does that include browser rendering time?
I don't mind inserting a few console.log here and there in the AngularJS source code to do that.
I'll probably lose reputation for saying this :) lets hope not - but the latest IE11 dev tools have both a profiler for scripts, and UI responsiveness tabs for rendering.
It's a little bit odd using IE for internet dev, but the dev tools have been really good - it has taken some adapting to switch from chrome, but it's just as useful and quite performant.
An overview of the profile and how to use it, and track down specific app areas is at http://msdn.microsoft.com/en-us/library/ie/dn255009(v=vs.85).aspx
I don't know if it can help your exact situation but Batarang (the chrome extension for AngularJS) can really help you with AngularJS performance measuring.
Like this: https://github.com/angular/angularjs-batarang#performance
You can find it here: https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en

Categories