DEV Community

Cover image for Optimizing Functional React Components
Adam Nathaniel Davis
Adam Nathaniel Davis

Posted on

51

Optimizing Functional React Components

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.


Image description

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>;
}


Enter fullscreen mode Exit fullscreen mode

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:

  1. This component accepts no props and seems to generate no side effects. So why isn't it memo-ized?

  2. Why are we using a state variable to track an internal value - clickCounter - that has no impact on the display?

  3. 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?

  4. Why are we allowing the helper functions to be re-defined every single time this component is called?

  5. Why are we creating a new function definition inside the mouseover event?

Image description

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.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read more →

Top comments (11)

Collapse
 
thethirdrace profile image
TheThirdRace

From the top of my head:

  1. Because memoization has a cost too. Unless there's something expansive in this component, you're gonna lose more performance to memoization than simply re-rendering the component as-is... If the component re-renders too many times, there's a problem upward the chain, not in this component

  2. Because that's very bad code. I don't see people creating state they don't need unless they're really poor devs, in which case no amount of good practice is gonna save them

  3. Recreating the object for styling isn't a problem per se, you're targeting an optimization that would shave maybe 5 nanoseconds... But, using inline-styles is really bad for browser performance which is actually mesurable with performance tools. Not to mention that inline styles can be high-jacked by malicious scripts and you should always block them with a Content Security Policy. You're 1000% better to import a CSS file and use classname instead. Not only does it solve the inline style performance, but it also makes your component reusable between projects.

  4. Again, unless you NEED to optimize, simply don't. You won't gain any advantage by using useCallback or other performance enhancing tools. Memoization has a cost that won't be alleviated by these helper functions unless you do something expansive, which is relatively rare...

  5. This is not a performance problem, but a bad coding practice. Even if you replaced the inline function with the function reference, performance is not gonna improve even 1 nano second... I totally agree with you that this is totally horrible syntax and show the dev has not understood how to pass functions around, but it doesn't impact your performance in the slightest in the end.

Collapse
 
miketalbot profile image
Mike Talbot ⭐ • Edited

One of the problems is that so many examples online and in documentation show things that use these patterns - probably because they are demonstrating a different point and doing it succinctly. Of course it then ends up looking "like best practice" when it patently isn't.

Side note, and not relevant to the points you are making here, but the game programmer in me silently screams when I code React, because I'm constantly making throw away things even if I AM avoiding the pitfalls you outline. (The angel on my right shoulder is always bellowing GC AIN'T FREE - into my ear).

Collapse
 
fjones profile image
FJones

There's also a huge lack of best-practices for large react apps. It's always very contained, small examples, simple functionality. At best, there's article #374 on which folder structure to use, or the thirteenth video on how to hyperoptimize a Counter.

In fact, this is the case for most JS ecosystems: express, react, nestjs...

Collapse
 
thethirdrace profile image
TheThirdRace

What irks me the most is when I see an article that shows an example with 1 value, but you know fully well it's not gonna hold when you introduce a 2nd value...

And no matter how many times you search, you have 1 billion article for that 1 value, but none whatsoever on how to implement the 2nd or 3rd value...

An example for 1 value is the worst kind of example you can get. Always strive to have at least 2 values so we understand how to tackle multiple values in an elegant way.

Collapse
 
ronnewcomb profile image
Ron Newcomb

I also don't write components as efficiently as I could, and then I remember why: it'll rarely make a difference except making the code more difficult to maintain.

Optimize for your humans and your manager's budget. You'd probably get more perf from simply swapping in Preact.

Collapse
 
matborowiak profile image
Mat Borowiak
Comment hidden by post author
Collapse
 
bytebodger profile image
Adam Nathaniel Davis

I guess you'd much rather that I add an "example" that has several thousands lines of code.

Yeah... that'll be a great read.

Collapse
 
matborowiak profile image
Mat Borowiak

Yes, but the things you have done to this component are not going to make it slower. And your supposed optimisation will show no effect.

Prove me wrong using codesanbox or something else. Feel free to write another component, prove the case exists, and let's see then how to improve it. Otherwise this is pointless - and thats my point.

I know from experience of working in alrge frontend systems, there are very rare scenarios React has difficulties to handle that majority will never stubmle upon. In general talking about "performance improvements" in frontend in a context of frameworks like React is just... I don't know. Prove the case exists, as doing unnecessery things is very much against actual engineering principals.

I honestly think once you show actual setup that may start choking 99% will realise they don't need to care about that one scenario at all.

Thread Thread
 
bytebodger profile image
Adam Nathaniel Davis

AWESOME FEEDBACK!!!

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Well written informative article..

Some comments have been hidden by the post's author - find out more

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more