Embedding Data Into React/JSX Elements
Tags: react, javascript, webdev, tutorial
Published: 2023-03-22 01:03:10

What I'm about to show you is really pretty basic.  So Code Gurus out there can feel free to breeze on by this article.  But I've rarely seen this technique used, even in "established" codebases crafted by senior devs.  So I decided to write this up.

This technique is designed to extract bits of data that have been embedded into a JSX (or... plain ol' HTML) element.  Why would you need to do this?  Well... I'm glad you asked.
<br/><br/>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5oqpbs5195vl12h69jdr.jpg)
## The Scenario
Let's look at a really basic function:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td key={`cell-${rowIndex}-${cellIndex}`}>
            {paintIndex}
         </td>
      );
   })
}
```

This function simply builds a particular row of table cells.  It's used in my https://paintmap.studio app to build a "color map".  It generates a giant grid (table) that shows me, for every block in the grid, which one of my paints most-closely matches that particular block.

Once this feature was built, I decided that I wanted to add an `onClick` event to each cell.  The idea is that, when you click on any given cell, it then highlights every cell in the grid that contains the same color as the one you've just clicked upon.

Whenever you click on a cell, the `onClick` event handler needs to understand _which_ color you've chosen.  In other words, you need to _pass_ the color _into_ the `onClick` event handler.  Over and over again, I see code that looks like this to accomplish that kinda functionality:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={paintIndex => handleCellClick(paintIndex)}
         >
            {paintIndex}
         </td>
      );
   })
}
```

The code above isn't "wrong".  It will pass the `paintIndex` to the event handler.  But it isn't really... _optimal_.  The inefficiency that arises is that, for every single table cell, we're creating a brand new function definition.  That's what `paintIndex => handleCellClick(paintIndex)` does.  It spins up an entirely new function.  If you have a large table, that's a lotta function definitions.  And those functions need to be redefined not just on the component's initial render, but _whenever this component is re-invoked_.

Ideally, you'd have a _static_ function definition for the event handler.  That would look something like this:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
```

In the above code, the `handleCellClick` has already been defined outside this function.  So React doesn't need to rebuild a brand new function definition every single time that we render a table cell.  Unfortunately, this doesn't entirely work either.  Because now, every time the user clicks on a table cell, the event handler will have no idea which _particular_ cell was clicked.  So it won't know which `paintIndex` to highlight.

Again, the way I _normally_ see this implemented, even in well-built codebases, is to use the `paintIndex => handleCellClick(paintIndex)` approach.  But as I've already pointed out, this is inefficient.  

So let's look at a couple ways to remedy this.
<br/><br/>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wtcoy6ls3pb76oq4qp01.jpg)
## Wrapper Components
One approach is to create a wrapper component for my table cells.  That would look like this:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <MyTableCell
            key={`cell-${rowIndex}-${cellIndex}`}
            paintIndex={paintIndex}
         >
            {paintIndex}
         </MyTableCell>
      );
   })
}
```

In this scenario, we're no longer using the base HTML attribute of `<td>`.  Instead, there's a custom component, that will accept `paintIndex` as a prop, and then presumably use that prop to build the event handler.  `MyTableCell` would look something like this:

```javascript
const MyTableCell = paintIndex => {
   const handleCellClick = () => {
      // event handler logic using paintIndex
   }
   
   return (
      <td onClick={handleCellClick}>
         {paintIndex}
      </td>
   )
}
```

Using this approach, we don't have to pass the `paintIndex` value _into the `handleCellClick` event handler_, because we can simply reference it from the prop.  There's much to like in this solution because it's consistent with an idiomatic approach to React.  Ideally, you'd even _memoize_ the `MyTableCell` component so it doesn't get remounted (and re _rendered_) every time we build a table cell that uses the same paint color.

However, this approach can also feel a bit onerous because we're cranking out another component purely for the sake of making that one `onClick` event more efficient.  Also, if the `handleCellClick` event handler needs to do other logic that impacts that state _in the calling component_, the resulting code can get a bit "heavy".

Sometimes you want the logic for that event handler to be handled right inside the calling component.  Luckily, there are other ways to do this.
<br/><br/>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/krr0uiuawld3q5jgwl71.png)
## HTML Attributes
HTML affords us a lot of freedom to "stuff" data where it's needed.  For example, you could use the `longdesc` attribute to embed the `paintIndex` right into the HTML element itself.  Unfortunately, `longdesc` is only "allowed" in `<frame>`, `<iframe>`, and `<img>` elements.

Granted, browsers are tremendously forgiving about the usage of HTML attributes.  So if you were to start putting `longdesc` attributes on all sorts of "illegal" HTML elements, it really won't _break_ anything.  The browser will basically just _ignore_ the non-idiomatic attributes.  In fact, you can even add your own _custom_ attributes to HTML elements.  

Nevertheless, it's usually good practice to avoid stuffing a buncha non-allowed or completely-custom attributes into your HTML elements.  But we have more options.  More "standard" options.

**(Near) Universal HTML Attributes**

If you wanna find an attribute that you can put on pretty much any element, the first things is to look at the attributes that are allowed in (almost) any elements.  They are as follows:

- `id`
- `class`
- `style`
- `title`
- `dir`
- `lang` / `xml:lang`

The nice thing about these attributes is that you can pretty much use them anywhere within the _body_ of your HTML, on pretty much _any_ HTML element, and you don't have to worry about whether they're "allowed".  You can, for example, put a `title` attribute on a `<div>`, or a `dir` attribute on a `<td>`.  It's all "acceptable" - by HTML standards, that is.

So if you wanted to use one of these attributes to "pass" data into an event handler, what would be the best choice?

**`title`**

First of all, as tempting as it may be to use something like `title`, I would _not_ recommend this.  `title` is used by screen readers and you're gonna jack up the accessibility of your site if you stuff a bunch of programmatic data into that attribute - data that should _not_ be read by a screen reader.

**`dir`, `lang`, `xml:lang`**

Similarly, you should avoid appropriating the `dir`, `lang`, or `xml:lang` attributes.  Messing with these attributes could jack up the utility of the site for international users (i.e., those who are using your site _with a different language_).  So please, leave those alone as well.

**`style`**

Also, you could try to cram "custom" data into a `style` attribute.  But IMHO, that's gonna come out looking convoluted.  _In theory_, you could define a custom style property like this:

```javascript
<td style={{paintIndex}}>
```

Then you could try to read this made-up style property on the element when it's captured in the event handler.  But... I don't recommend such an approach.  Not at all.  First, if you have any _legitimate_ `style` properties set on the element, you're gonna end up with a mishmash of _real_ and _made-up_ properties.  Second, there's no reason to embed data in such a verbose format.  You can do it much "cleaner" with the other options at our disposal.

**`id`**

The `id` attribute can be a great place to "embed" data.  Here's what that would look like:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            id={paintIndex}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
```

Here, we're using the `paintIndex` _as the `id`_.  Why would we do this?  Because then we can create an event handler that looks like this:

```javascript
const handleCellClick = (event = {}) => {
   uiState.toggleHighlightedColor(event.target.id);
}
```

This works because the synthetic event that's passed to the event handler will have the `id` of the clicked element embedded within it.  This allows us to use a generic event handler on each table cell, while still allowing the event handler to understand exactly _which_ `paintIndex` was clicked upon.

This can still have some drawbacks.  First of all, `id`s are supposed to be _unique_.  In the example above, a given `paintIndex` may be present in a single table cell - or in hundreds of them.  And if we simply use the `paintIndex` value as the `id`, we'll end up with many table cells that have identical `id` values.  (To be clear, having duplicate `id`s won't break your HTML _display_.  But in some scenarios it can break your JavaScript _logic_.)

Thankfully, we can fix that, too.  Notice that our table cells have `key` values.  And keys _must_ be unique.  In this scenario, I addressed that problem by using the row/cell counts to build the key.  Because no two cells will have the same combination of row/cell numbers.  We can add the same format to our `id`.  That looks like this:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            id={`cell-${rowIndex}-${cellIndex}-${paintIndex}`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
```

Now, there will never be a duplicate `id` on any of our cells.  But we still have the `paintIndex` value embedded _into_ that `id`.  So how do we extract the value in our event handler?  That looks like this:

```javascript
const handleCellClick = (event = {}) => {
   const paintIndex = event.target.id.split('-').pop();
   uiState.toggleHighlightedColor(paintIndex);
}
```

Since we wrote this code, and since we determined the naming convention for the `id`, we also know that the `paintIndex` value will be the last value in a string of values that are delimited by `-`.  If we `split('-')` that string and then `pop()` the last value off the end of it, we know that we're getting the `paintIndex` value.

**`class`**

`class` is also a great place to "embed" data - even if it doesn't map to any CSS class that's available to the script.  If you're familiar with jQuery UI, you've probably seen many instances where `class` is used as a type of "switch" that doesn't actually drive CSS styles.  Instead, it tells the JavaScript code _what to do_.

Of course, in JSX we don't use `class`.  We use `className`.  So that solution would look like this:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`${paintIndex}`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
```

And the event handler looks like this:

```javascript
const handleCellClick = (event = {}) => {
   uiState.toggleHighlightedColor(event.target.className);
}
```

Just as we previously grabbed `paintIndex` from the event object's `id` field, we're now grabbing it from `className`.  How would this work if you also had "real" CSS classes in the `className` property?  That would look like this:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`cell ${paintIndex}`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
```

And the event handler would look like this:'

```javascript
const handleCellClick = (event = {}) => {
   const paintIndex = event.target.className.split(' ').pop();
   uiState.toggleHighlightedColor(paintIndex);
}
```

The "cell" class on the `<td>` is a "real" class - meaning that it maps to predefined CSS properties.  But we also embedded the `paintIndex` value into the `className` property and we extracted it by splitting the string on empty spaces.

To be fair, this approach may feel a bit... "brittle".  Because it depends upon the `paintIndex` value being the last value in the space-delimited `className` string.  If another developer came in and added another CSS class to the end of the `className` field, like this:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`cell ${paintIndex} anotherCSSClass`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
```

The logic would _break_.  Because the event handler would grab `anotherCSSClass` off the end of the string - and try to treat it like it's the `paintIndex`.  If you'd like to make it a bit more robust, you can change the logic to something like this:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            className={`cell paintIndex-${paintIndex} anotherCSSClass`}
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={handleCellClick}
         >
            {paintIndex}
         </td>
      );
   })
}
```

And then update the event handler like this:

```javascript
const handleCellClick = (event = {}) => {
   const paintIndex = event.target.className
      .split(' ')
      .find(className => className.includes('paintIndex-'))
      .split('-')
      .pop();
   uiState.toggleHighlightedColor(paintIndex);
}
```

By doing it this way, the value that's extracted for `paintIndex` isn't dependent upon being the _last_ item in the space-delimited string.  It can exist anywhere inside the `className` property, as long as it's prepended with "paintIndex-".
<br/><br/>


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iwc7mjwje7c67omif9pw.jpg)
## Why Should You Care?
To be frank, in small apps, having something like this isn't exactly a federal crime:

```javascript
const getTableCells = (cells = [rgbModel], rowIndex = -1) => {
   return cells.map((cell, cellIndex) => {
      const paintIndex = colors.findIndex(color => color.name === cell.name);
      return (
         <td
            key={`cell-${rowIndex}-${cellIndex}`}
            onClick={paintIndex => handleCellClick(paintIndex)}
         >
            {paintIndex}
         </td>
      );
   })
}
```

The performance "hit" you incur by defining a new function definition inside the `onClick` property is... minimal.  In some cases, trying to "fix" it could be understandably defined as a "micro-optimization".   But I do believe it's a solid practice to get in the habit of avoiding these whenever possible.  

When the event handler doesn't need to have information passed into it from the clicked element, it's a no-brainer to keep arrow functions out of your event properties.  But when it _does_ require element-specific info, too often I see people blindly fall back on the easy method of dropping arrow functions into their properties.  But there are many ways to avoid this - and they require little additional effort.