Legitimate request: can you provide some actual benchmarks or examples that demonstrate these perf issues?
Not straightforwardly (due to pseudonymity and in any case client confidentiality) but I can give you a typical example.
I've worked on quite a few web apps in recent years that have used SVGs to draw somewhat complicated and interactive visualisations. That needs a level of efficiency well beyond your basic "Here's a control, here's the underlying data in the model, there's roughly a 1:1 correspondence between them" web app UI. For example, some of the layout algorithms could take a significant fraction of a second just to run once, so typically you get a whole extra level of cache sitting between your underlying data and your React-based rendering functionality. Even then, a rerender of the whole visualisation can easily take a significant fraction of a second, just due to the number of elements involved and the relatively poor performance of browsers at handling large inline SVGs in various situations.
So, you immediately need to start isolating which parts of the SVG actually need updating in response to user events, including relatively frequent ones like mouse moves or repeated keypresses. But if many of those child components are also going to be interactive, there are potentially hundreds or thousands of event handlers flying around. If each type of interaction needs a handler that has some common information, like say where your underlying controller logic lives, and also something specific to each element, like say the ID(s) of some underlying item(s) in your data model that are related to that part of the visualisation, then either you get a lot of binding going on or you have to pass in a common function and manually call it with the extra parameters from the event handler functions for your SVG elements.
One thing you really don't want to do in this situation is have function involved that is (a) recreated on each render of (b) a high-level component with a lot of children that will ultimately be calling that function. Doing so will inevitably mean that you need to rerender all such children in your viz instead of just a very small number that might be relevant to say a particular mouse move or keyboard event.
The thing that gets me is that in this sort of environment, whether it's an SVG or a table or a list or any other part of the DOM that will contain a lot of content and probably be based on a lot of different underlying data points in your model, it's entirely predictable that you will have to focus on only narrow parts of your rendered content in response to frequent user-triggered events. So you know right from the start that you can't just bind your event handling functions at the top level and pass them down through props and add more specific parameters as you pass them into more specific child components. That entire strategy is doomed before your write a line of code (though unfortunately it will probably work just fine if you're developing with a very small set of data and a very simple resulting visualisation, which can create a false sense of confidence if you're not careful). So you might as well plan the design for your event handling and callbacks around what you're inevitably going to need from the start. I can't actually give you hard data on the exact FPS we saw the first time we tried the bad way of doing this, but I can tell you that it was more like SPF than FPS.
Ryan did also address the `shouldComponentUpdate / PureComponent` aspect in the post already.
Honestly, the last parts of the article about ignoring handlers didn't make much sense to me anyway. I can't see why you'd ever want to pass props into a React component that weren't ultimately going to influence rendering one way or another, and therefore why you'd ever be in a position where you could safely ignore props for shouldComponentUpdate purposes yet wouldn't just remove them altogether. Certainly any assumptions about event-handling on<X> not needing to be diffed would be totally wrong in the context of a table/list/SVG/etc. where each child element is tied to some specific underlying data point(s) and has event handler functions supplied via props accordingly. That looked like another of those cases where the author is generalising their own limited experience and failing to realise that for plenty of other situations the same assumptions won't apply.
Thanks for the reply and the details. Let me see if I can add more context to the overall discussion, and why Ryan wrote the article in the first place.
First, the author of the article is Ryan Florence. He's a very well-known name in the React community, as he's co-author of React-Router, and co-owner of the React Training company. He makes a living teaching React to developers at different companies. I don't always agree with _everything_ he says, but he has a _lot_ of personal experience using React, and has seen a wide variety of ways that people are learning and using React in the real world. So, I don't think it's fair to say that he's "generalising his own limited experience".
Second, I would say that Ryan's post isn't trying to argue that you should never-ever worry about creating functions in `render()`. It _is_ trying to say that that has become a blanket concern that people blindly repeat, no matter what the actual performance concerns are for a given component, and that some of the possible origins for that concern are likely no longer an issue with modern JS engines.
For example, I know that AirBnB's ESLint config, which is very popular, turns on the "react/jsx-no-bind" rule [0] by default. This rule flat-out forbids using `fn.bind()` inside of JSX constructs, but does not take into account anything like how the component is actually being used. A component rendered once at the top level of an app is going to have a vastly different performance profile than a nested component in the middle of a hot UI code path.
I've seen learners who are still trying to grasp the basics of React being told "oh, you should never use an arrow function in `render()`, that's slow". Even if that's true, that's also not the advice the learner needs at that time. The most common example of this I see is someone who wants to pass a parameter to a callback, and they do:
onClick={this.handleClick("stuff")}
That of course fails, because the click handler is being called immediately during rendering. The simplest fix for this is to just make it an arrow function that closes over the call:
onClick={() => this.handleClick("stuff")}
Even if that isn't the most absolutely performant approach, it _works_, it's simple for the learner to implement, and it solves their immediate problem. They don't _need_ to be worrying about perf at that level of understanding.
Now, as you said: _if_ you do have an extremely hot and perf-sensitive UI path, then yes, it's worth worrying about the details like this. But, the point of the article is that the React community has gotten in the habit of blindly repeating "DON'T DO THAT EVER!!!!!", when the reality is much more nuanced: "Don't worry about that.... _yet_".
To specifically answer your last aspect: Let's say you've got a child component that implements a click handler like this:
In that example, `this.props.doStuff` isn't actually affecting rendering. So, if the parent component passes in a new function reference for `props.doStuff`, the component doesn't actually need to re-render - the click handler would behave the same way regardless.
First, the author of the article is Ryan Florence. He's a very well-known name in the React community, as he's co-author of React-Router, and co-owner of the React Training company.
I appreciate that. However, there are plenty of genuine experts with low profiles, and unfortunately there are also plenty of high-profile trainers who have relatively little recent, deep experience as practitioners because they're constantly jumping from one context to another. Whenever possible, I prefer to consider writing objectively on its merits, rather than by who wrote it.
Just to be clear on one point, though, when I wrote "his own limited experience", that wasn't intended as any sort of insult or criticism, merely a statement of fact. It's a huge industry, and no-one can possibly know everything about how a library like React is being used or what experience has been collected by the numerous other people using it in different ways. Authors who write as if they do are a pet peeve of mine, particularly when, as in this case, they do so in a way that implies others who might disagree are automatically wrong.
It _is_ trying to say that that has become a blanket concern that people blindly repeat, no matter what the actual performance concerns are for a given component, and that some of the possible origins for that concern are likely no longer an issue with modern JS engines.
I agree that we should be careful about dogma, and we should be careful about relying on data or assumptions that are no longer relevant. These are perennial issues in tech fields, and caution is wise.
However, I think it's also wise to remember that not everyone is running the latest and greatest browsers and JS engines. For example, there are many web sites used in businesses where desktops will be locked to a specific and possibly older browser to ensure compatibility with their other in-house tools.
I've seen learners who are still trying to grasp the basics of React being told "oh, you should never use an arrow function in `render()`, that's slow". Even if that's true, that's also not the advice the learner needs at that time.
There is a more general point about teaching here, I think. My own experience from teaching a variety of fields over the years is that showing a simplified version of something is often helpful for beginners, but showing an inaccurate, misleading or exaggerated version of something can be dangerous.
That same beginner who learns to casually build new functions by default in this sort of situation is, almost by definition, not going to know enough to realise when doing so in a more demanding context might be dangerous, and there is no guarantee that anything or anyone will arrive at a more suitable point in their development later to broaden their understanding or warn them about any implicit assumptions behind what they learned before.
In this case, clearly I disagree with the author about there not being any data to support concerns about passing new functions as props in this way, and I also think he exaggerates the cost of avoiding that problem by using any of the common alternative idioms in such cases. So I don't think it actually is a very good idea to teach beginners that this is OK and write off the concerns about performance so definitively as some sort of mere historical point that no longer applies.
To specifically answer your last aspect: Let's say you've got a child component that implements a click handler like this: [...] In that example, `this.props.doStuff` isn't actually affecting rendering.
OK, fair enough, that's a valid case. Ironically, it's also one I should have allowed for in what I wrote before: I seem to have inadvertently edited out a paragraph from an earlier comment about the alternative strategies we use, but one of the specific examples I gave was having props include both a callback function without pre-bound parameters and separately an ID value. If you can accept the arguably undesirable dependency of having the component's own event handling code always call the callback function with the ID as its parameter, thus allowing both the callback function and the ID to remain strictly equal in props from one render to the next if nothing is actually changing, then you wind up with code essentially the same as your handleClick example, and yes, if you're careful then you could skip checks on callback functions in shouldComponentUpdate that were only used in that way.
Not straightforwardly (due to pseudonymity and in any case client confidentiality) but I can give you a typical example.
I've worked on quite a few web apps in recent years that have used SVGs to draw somewhat complicated and interactive visualisations. That needs a level of efficiency well beyond your basic "Here's a control, here's the underlying data in the model, there's roughly a 1:1 correspondence between them" web app UI. For example, some of the layout algorithms could take a significant fraction of a second just to run once, so typically you get a whole extra level of cache sitting between your underlying data and your React-based rendering functionality. Even then, a rerender of the whole visualisation can easily take a significant fraction of a second, just due to the number of elements involved and the relatively poor performance of browsers at handling large inline SVGs in various situations.
So, you immediately need to start isolating which parts of the SVG actually need updating in response to user events, including relatively frequent ones like mouse moves or repeated keypresses. But if many of those child components are also going to be interactive, there are potentially hundreds or thousands of event handlers flying around. If each type of interaction needs a handler that has some common information, like say where your underlying controller logic lives, and also something specific to each element, like say the ID(s) of some underlying item(s) in your data model that are related to that part of the visualisation, then either you get a lot of binding going on or you have to pass in a common function and manually call it with the extra parameters from the event handler functions for your SVG elements.
One thing you really don't want to do in this situation is have function involved that is (a) recreated on each render of (b) a high-level component with a lot of children that will ultimately be calling that function. Doing so will inevitably mean that you need to rerender all such children in your viz instead of just a very small number that might be relevant to say a particular mouse move or keyboard event.
The thing that gets me is that in this sort of environment, whether it's an SVG or a table or a list or any other part of the DOM that will contain a lot of content and probably be based on a lot of different underlying data points in your model, it's entirely predictable that you will have to focus on only narrow parts of your rendered content in response to frequent user-triggered events. So you know right from the start that you can't just bind your event handling functions at the top level and pass them down through props and add more specific parameters as you pass them into more specific child components. That entire strategy is doomed before your write a line of code (though unfortunately it will probably work just fine if you're developing with a very small set of data and a very simple resulting visualisation, which can create a false sense of confidence if you're not careful). So you might as well plan the design for your event handling and callbacks around what you're inevitably going to need from the start. I can't actually give you hard data on the exact FPS we saw the first time we tried the bad way of doing this, but I can tell you that it was more like SPF than FPS.
Ryan did also address the `shouldComponentUpdate / PureComponent` aspect in the post already.
Honestly, the last parts of the article about ignoring handlers didn't make much sense to me anyway. I can't see why you'd ever want to pass props into a React component that weren't ultimately going to influence rendering one way or another, and therefore why you'd ever be in a position where you could safely ignore props for shouldComponentUpdate purposes yet wouldn't just remove them altogether. Certainly any assumptions about event-handling on<X> not needing to be diffed would be totally wrong in the context of a table/list/SVG/etc. where each child element is tied to some specific underlying data point(s) and has event handler functions supplied via props accordingly. That looked like another of those cases where the author is generalising their own limited experience and failing to realise that for plenty of other situations the same assumptions won't apply.