With lots of textareas, my (admittedly large) form becomes sluggish.
With text input fields instead, however, everything is snappy.
I've created two minimized testcases, one with textareas and one with input fields. Click the toggle button to show/hide a group to see the issue. With Chrome, at least, the group is really slow to toggle in the textarea version.
2500 textareas: http://jsbin.com/wudujusoji/edit?html,js,output
2500 text input fields: http://jsbin.com/rezudotojo/edit?html,js,output
What am I doing wrong here? Shouldn't the textarea and text input field versions be roughly the same performance?
Let's make the test reasonable and add a shouldComponentUpdate to the Input component in each.
var Input = React.createClass({
shouldComponentUpdate: function(nextProps){
return nextProps.value !== this.props.value;
},
console.time()
is good if you're trying to measure some math or similar, but to really see what's happening here we need to use the profiler.
We'll start with the <input>
s. I did this a few times, and it's always about 14ms for me.
The gray bar to the right labeled '(program)' is the browser doing its own work after the JS has returned. Here it's taking about 17ms to prepare for rendering. After that is idle where it's doing pretty much nothing except painting. Cool, this isn't bad considering the number of elements.
The textarea is much more interesting. React averages around 30ms in these, which is fine considering it's the development build. I have no idea why chrome is taking 500ms to process these changes.
I thought React must be doing something to cause this, so I (hackily) added a MutationObserver.
var mObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation, i) {
console.log('mutation ' + i + ': '
+ mutation.type
+ ', at '
+ mutation.target.parentNode.tagName
+ ' > '
+ mutation.target.tagName
+ '['
+ [].indexOf.call(mutation.target.parentNode.children, mutation.target)
+ ']'
+ ', on attribute ' + mutation.attributeName
+ ', from ' + mutation.oldValue
+ ' to ' + mutation.target.getAttribute(mutation.attributeName)
);
});
});
var moConfig = { attributes: true, childList: true, characterData: true, subtree: true };
React.render(React.createElement(Editor, null),
document.querySelector('.editor'),
function(){
var root = document.getElementById('root');
if (!root) throw new Error('no root');
mObserver.observe(root, moConfig);
}
);
And when clicking the button it shows this:
mutation 0: attributes, at DIV > DIV[0], on attribute class, from null to category
mutation 1: childList, at DIV > BUTTON[0], on attribute null, from null to null
So yeah... I'm pretty sure changing the class name of a div and the text content of the button causes it to spend 500ms updating. My guess is it's all spent trying to layout the textareas.
As always, the answer to "Rendering 2,000 nodes when I do X" is to not render 2,000 nodes; X (e.g. React) doesn't matter.
Actually, slightly over 5,000 here, but who's counting?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With