DEV Community

Cover image for 99% of frontend devs don't use this
Antonio Moruno Gracia
Antonio Moruno Gracia

Posted on

99% of frontend devs don't use this

In the React world, most developers don’t think twice before using closures in their component render methods, especially when mapping over lists.

But what if I told you there's an underutilized, yet powerful alternative that can help with performance, readability, and even better integration with tools?

Let’s talk about HTML data-* attributes: a feature that’s rarely used by frontend developers, but deserves a second look.

❓ What are data-* Attributes?

HTML’s data-* attributes allow you to embed custom data inside DOM elements. In plain HTML:

<div data-user-id="123" data-role="admin">John</div> 
Enter fullscreen mode Exit fullscreen mode

In React, you can use them the same way:

<div data-user-id={user.id} data-role={user.role}> {user.name} </div> 
Enter fullscreen mode Exit fullscreen mode

They are accessible in event handlers via the dataset object:

e.currentTarget.dataset.userId; 
Enter fullscreen mode Exit fullscreen mode

🧠 The Common Pattern: Closures in .map()

Let’s say you’re rendering a list of items:

{items.map((item) => ( <button key={item.id} onClick={() => handleClick(item.id)}> {item.name} </button> ))} 
Enter fullscreen mode Exit fullscreen mode

This works fine. It’s readable and easy to write.

But behind the scenes, you're creating a new function for each item on every render: a closure that captures item.id.

In many apps, this has no practical impact. However...

⚠️ The Downsides of Closures

While closures are a fundamental part of JavaScript and React, using them inside .map() can have drawbacks:

1. Unnecessary Re-Renders

If you're using React.memo, React.useCallback, or virtualized lists (like react-window), new function references can cause unwanted re-renders. Since the function is recreated every time, memoization doesn't help.

2. Harder to Optimize

Imagine you're building a highly interactive list that renders hundreds of items. Minimizing re-renders becomes important, and inline closures can work against that.

✅ The Alternative: data-*

Instead of creating a closure for each item, attach metadata directly to the DOM using data-*:

function handleClick(e) { const id = e.currentTarget.dataset.id; console.log("Clicked item:", id); } {items.map((item) => ( <button key={item.id} data-id={item.id} onClick={handleClick}> {item.name} </button> ))} 
Enter fullscreen mode Exit fullscreen mode
  • Single function reference → plays nicely with React.memo or React.useCallback

  • Improved performance for large lists or re-render-sensitive components

🤔 So Why Isn’t Everyone Doing This?

Because closures are easy, intuitive, and performant enough for most apps. data-* feels a bit "old-school," and it’s rarely mentioned in modern React/Frontend tutorials.

But in situations where performance or memoization matters, this approach can be a hidden gem.

✨ In Summary

While data-* attributes aren’t a common tool in a React developer’s toolbox, they offer real advantages in specific scenarios:

  • Reduce unnecessary function creations

  • Improve memoization and rendering performance

  • Enable simpler and cleaner event handling

They’re not a replacement for closures, but they’re a great alternative when performance or architecture calls for it.

Have you ever used data-* attributes like this? Or do you usually stick with closures? I’d love to hear your thoughts and experiences!

I hope you found this post interesting. Let me know what you think in the comments 👇

Top comments (44)

Collapse
 
quozzo profile image
Quozzo

Im not a fan of this approach because only serialisable data can be inserted into the DOM and then extracted back out into JS, and while you can simply use the ID to find the item in the array but doing so will cause so many loops that you lose any performance gained by removing the closures.

While always, it depends on the situation, so it's nice to have it in your pocket should the situation arise.

Collapse
 
js2me profile image
Sergey S. Volkov • Edited

new function references can cause unwanted re-renders

That's why functional components in React are sucks.

Also most React developers do not even know that useMemo useEffect useState and all other React hooks it is not only "magic hook" - this is functions which do some calculations inside.

Take a look at this great article

mbrizic.com/blog/react-is-insane/

I personally try to get rid of using React hooks, I'm using MVVM and MobX which works with classes

Collapse
 
js2me profile image
Sergey S. Volkov

Also if we will use data-* attributes then this is killing any types in TypeScript, so this is not good.

Collapse
 
js2me profile image
Sergey S. Volkov • Edited

Also modifying data-* attributes is not performant (calls reflow after change)

Collapse
 
bigcheeseh profile image
bigcheeseh

Every class component should have render function with all drawbacks of function components and without ability to use hooks but only terrible on componentShouldRecieveProps, already ReceivedProps, maybeItsHereTimeToHandlePropsButMaybeNot, componentWillUnmount etc. Do not remember exact names but it was very close to it

Collapse
 
js2me profile image
Sergey S. Volkov

Yes, agreed with you, but in hook way you must keep in mind how works useEffect with dependencies and how works closures in use* hooks. How simple works functional component and why declaration a lot of hooks in functional component body can be hurtful for performance.

Collapse
 
michael_1c56c1490fd191f72 profile image
Michael

This is a terrible article lol

Collapse
 
cmacu profile image
Stasi Vladimirov

Hard disagree. This article is extremely accurate representation of what 90% of enterprise and startup codebases contain right now. Anyone who denies it either has no experience or is too far the react cult to realize what’s happening.

Thread Thread
 
spock123 profile image
Lars Rye Jeppesen

So much this

Collapse
 
js2me profile image
Sergey S. Volkov

Why ?

Thread Thread
 
michael_1c56c1490fd191f72 profile image
Michael

React has plenty of issues, but he’s criticising React for problems he created himself because he doesn’t understand it and write shit code

Thread Thread
 
moruno21 profile image
Antonio Moruno Gracia

I’m not criticizing React. Actually I love it. I’m just presenting another way of doing things 😙

Thread Thread
 
spock123 profile image
Lars Rye Jeppesen

You should. at this moment, frameworks like Vue or Angular are now better and more modern / performant than React.

Collapse
 
geregur profile image
GEREGUR

youtu.be/DLa2FQQzCr4?si=rSh0tlV-R5...

tldr: this is not a great article

Collapse
 
cmacu profile image
Stasi Vladimirov

That video demonstrates and perfectly proves the points in the article by showcasing snippets from his own code that contain useEffect hooks and also sharing examples from large projects that contain similar problems…

TLDR: that video is an amazing demonstration why the article is accurate and how blind React worshippers are for their own misery.

Collapse
 
chinmaymoghe profile image
Chinmay

I am inclined to comment, just use Svelte and be happy 😁

Collapse
 
aaron1 profile image
Aaron

It's also not typesafe, and for large data or long lists you will be serialising the data into strings ahead of time, before you even need it. It's cheaper to make a new anonymous function than serialising large data sets to string.
Keeping the data in memory and only accessing when needed is sometimes more preferable.

Collapse
 
bernardigiri profile image
Bernard Igiri

With the rise of SSR this may become more important, however I remember the time when getting data out of the DOM was a big push. The thing is, you always want a single source of truth for your data. If you put things in the DOM and have things in memory, you will have to figure which one to trust when they fall out of sync. Moving everything to JavaScript is easy, because AJAX goes straight to JS. The DOM cannot query the server and only understands strings. So if the DOM is your source of truth, how are you going to get data into and out of it? Will it be able to keep up?

Collapse
 
paulvz_ao profile image
Paul Von Zeuner • Edited

I like this, I can see the uses for it.
But like everything in writing code, there are many ways to skin a cat.
And as engineers, we need to be aware of the security risks and use this with caution.

OK for UI-related non-sensitive data
Not OK for anything security-sensitive (roles, tokens, permissions)

Safe Use Example:
<div data-product-id={product.id} onClick={handleClick}>{product.name}</div>
function handleClick(e) {
const id = e.currentTarget.dataset.productId;
// Use it to fetch more info securely from backend
}

Unsafe Example:
<div data-auth-token={user.token}>...</div> // Don't leak sensitive data
<div data-role="admin" onClick={showAdminPanel}> // Role-based access is not secure on frontend

Collapse
 
zachbryant profile image
Zach • Edited

you're creating a new function for each item on every render

This isn't quite true. A new function is created each render, but this is the only function that gets called for every item map goes over, not per ID.

Collapse
 
poetro profile image
Peter Galiba

For buttons, checkboxes, radio buttons you could also use the name and value attributes.

Collapse
 
parag_nandy_roy profile image
Parag Nandy Roy

Such a simple trick but so underrated..

Collapse
 
b_maj_6cfdf9e36c5a12cf188 profile image
B Maj

I couldn’t get past the hyperbolic, made up statistic “99% of devs don’t do…” 😆

Some comments may only be visible to logged-in visitors. Sign in to view all comments.