Optimizing Functional React Components
Adam Nathaniel Davis
Posted on February 9, 2023
I've been writing functional React components for several years now. And while I feel that my code usually stands up to peer scrutiny, I've only recently become aware that there's a lot I could be doing that would be far more efficient.
I'm not talking about optimizing the way that I write code (e.g., writing fewer lines of code). I'm talking about optimizing the way that the code runs.
Nearly every day I encounter functional components (many of which were written by... me) that could honestly run in a much more efficient manner. But they don't, because they were written in a very... "basic" style, with little concern for optimization.
What do I mean by "inefficient"?
For the purposes of this article, I'm not talking about general inefficiencies that you may find in any code (e.g., nested loops). Nor am I talking about things that may specifically be considered inefficient in JavaScript code (e.g., unnecessary DOM manipulations). Instead, I'm talking about cases where the React team has given us the tools to optimize our code - but... we're simply not using them.
Consider the following basic example:
export const MyComponent = () => {
const [clickCounter, setClickCounter] = useState(0);
const onClick = event => {
// increment clickCounter and
// process the onClick event
}
const onMouseOver = event => {
// do some mouseover stuff
}
const postClick = () => {
// check clickCounter and potentially
// do some post-click processing
}
const preClick = () => {
// check clickCounter and potentially
// do some pre-click processing
}
return <div>
<h1
onMouseOver={event => onMouseOver(event)}
style={{color: 'green'}}
>
My "Basic" Component
</h1>
<p style={{textAlign: 'right'}}>
<MyButton
onClick={onClick}
postClick={postClick}
preClick={preClick}
style={{fontSize: 'bold'}}
>
Click here
</MyButton>
</p>
</div>;
}
This is about as basic as React gets. I have a simple component - <MyComponent/>
- that's generating some simple JSX. That JSX in turn calls another custom component - <MyButton/>
- that presumably provides some sort of button-wrapper functionality. The child component - <MyButton/>
- allows you to pass in various callback functions based on whether you want something to be done pre-click, on-click, or post-click. It also accepts a style
prop so you can style the button.
On the surface, this component doesn't seem to be too egregious. But there are many built-in features of the library that we're simply not leveraging. Here are some of the questions you should be thinking when you look at this component:
This component accepts no props and seems to generate no side effects. So why isn't it memo-ized?
Why are we using a state variable to track an internal value -
clickCounter
- that has no impact on the display?Why are we using objects to denote
style
attributes, knowing that those objects will be seen, every time the function is invoked, as completely new objects?Why are we allowing the helper functions to be re-defined every single time this component is called?
Why are we creating a new function definition inside the mouseover event?
More to come...
I'm not going to tackle all of these issues in this article. Instead, I'm going to publish a mini-series that explains when (and why) to use memo()
, useMemo()
, useCallback()
, and useRef()
. I'm doing this because, quite frankly, in my past code, I haven't been using those features nearly enough.
Posted on February 9, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 15, 2023