Records & Tuples for React, way more than immutability
Tags: react, javascript, typescript
Published: 2020-08-07 17:47:57

---
title: Records & Tuples for React, way more than immutability
published: true
cover_image: https://sebastienlorber.com/content/posts/2020-07-31-records-and-tuples-for-react-developers/images/hero.png
canonical_url: https://sebastienlorber.com/records-and-tuples-for-react
description: ...and how they are much more than immutable data structures.
tags: react, javascript, typescript
---

**Records & Tuples**, a very interesting **[proposal](https://github.com/tc39/proposal-record-tuple)**, has just reached **[stage 2](https://twitter.com/robpalmer2/status/1286040041089904640)** at **TC39**.

They bring **deeply immutable data structures** to JavaScript.

But don't overlook their **equality properties**, that are <u>**VERY**</u> interesting for **React**.

A whole category of **React bugs** are related to **unstable object identities**:

- **Performance**: re-renders that could be avoided
- **Behavior**: useless effect re-executions, infinite loops
- **API surface**: unability to express when a stable object identity matters

I will explain the basics of **Records & Tuples**, and how they can solve **real world React issues**.

---

# Records & Tuples 101

This article is about Records & Tuples **<u>for React</u>**. I'll only cover the basics here.

They look like regular **Objects and Arrays**, with a **#** prefix.

```jsx
const record = #{a: 1, b: 2};

record.a;
// 1

const updatedRecord = #{...record, b: 3};
// #{a: 1, b: 3};


const tuple = #[1, 5, 2, 3, 4];

tuple[1];
// 5

const filteredTuple = tuple.filter(num => num > 2)
// #[5, 3, 4];
```

They are **deeply immutable** by default.

```jsx
const record = #{a: 1, b: 2};

record.b = 3;
// throws TypeError
```

They can be seen as **"compound primitives"**, and can be compared by value.

**VERY IMPORTANT**: two deeply equal records will **ALWAYS** return `true` with `===`.

```jsx
{a: 1, b: [3, 4]} === {a: 1, b: [3, 4]}
// with objects => false

#{a: 1, b: #[3, 4]} === #{a: 1, b: #[3, 4]}
// with records => true
```

We can somehow consider that the identity of a Record is its actual value, like any regular JS primitive.

This property has **deep implications for React**, as we will see.

They are interoperable with JSON:

```jsx
const record = JSON.parseImmutable('{a: 1, b: [2, 3]}');
// #{a: 1, b: #[2, 3]}

JSON.stringify(record);
// '{a: 1, b: [2, 3]}'
```

They can only contain other records and tuples, or primitive values.

```jsx
const record1 = #{
  a: {
    regular: 'object'
  },
};
// throws TypeError, because a record can't contain an object

const record2 = #{
  b: new Date(),
};
// throws TypeError, because a record can't contain a Date

const record3 = #{
  c: new MyClass(),
};
// throws TypeError, because a record can't contain a class

const record4 = #{
  d: function () {
    alert('forbidden');
  },
};
// throws TypeError, because a record can't contain a function
```

**Note**: you may be able to add such mutable values to a Record, by using Symbols as WeakMap keys ([separate proposal](https://github.com/tc39/proposal-symbols-as-weakmap-keys)), and [reference](https://github.com/tc39/proposal-record-tuple#could-i-box-a-pointer-to-an-object-and-put-that-in-a-record-or-tuple) the symbols in records.

Want more? Read the [proposal](https://github.com/tc39/proposal-record-tuple) directly, or this [article](https://2ality.com/2020/05/records-tuples-first-look.html) from Axel Rauschmayer.

---

# Records & Tuples for React

React developers are now used to **immutability**.

Every time you update some piece of state in an immutable way, you create **new object identities**.

Unfortunately, this immutability model has introduced a whole new class of bugs, and performance issues in React applications.
Sometimes, a component works correctly and in a performant way, **only under the assumption that props preserve identities** as most as they can over time.

I like to think about Records & Tuples as a convenient way to **make object identities more "stable"**.

Let's see how this proposal will **impact your React code** with practical use cases.

**Note**: there is a [Records & Tuples playground](https://rickbutton.github.io/record-tuple-playground/#eyJjb250ZW50IjoiaW1wb3J0IFJlYWN0IGZyb20gXCJodHRwczovL2Nkbi5za3lwYWNrLmRldi9yZWFjdFwiO1xuaW1wb3J0IFJlYWN0RE9NIGZyb20gXCJodHRwczovL2Nkbi5za3lwYWNrLmRldi9yZWFjdC1kb21cIjtcblxuXG5jb25zdCBIZWxsbyA9ICh7dXNlcn0pID0+IHtcbiAgcmV0dXJuIDxwPkhlbGxvIHt1c2VyLm5hbWV9PC9wPlxufTtcblxuY29uc3QgdXNlciA9ICN7bmFtZTogXCJTZWJhc3RpZW5cIn1cblxuUmVhY3RET00ucmVuZGVyKFxuICAgIDxIZWxsbyB1c2VyPXt1c2VyfS8+LFxuICAgIGRvY3VtZW50LmJvZHksXG4pO1xuXG4iLCJzeW50YXgiOiJoYXNoIiwiZG9tTW9kZSI6dHJ1ZX0=), that can run React.

### Immutability

Enforcing immutability can be achieved with recursive `Object.freeze()` calls.

But in practice, we often use the immutability model without enforcing it too strictly, as it's not convenient to apply `Object.freeze()` after each update. Yet, mutating the React state directly is a common mistake for new React developers.

The Records & Tuples proposal will **enforce immutability**, and prevent common state mutation mistakes:

```jsx
const Hello = ({ profile }) => {
  // prop mutation: throws TypeError
  profile.name = 'Sebastien updated';

  return <p>Hello {profile.name}</p>;
};

function App() {
  const [profile, setProfile] = React.useState(#{
    name: 'Sebastien',
  });

  // state mutation: throws TypeError
  profile.name = 'Sebastien updated';

  return <Hello profile={profile} />;
}
```

### Immutable updates

There are [many ways](https://dev.to/sebastienlorber/insight-3-use-immerjs-over-lodash-set-immutablejs-or-plain-js-36bl) to perform immutable state updates in React: vanilla JS, Lodash set, ImmerJS, ImmutableJS...

Records & Tuples support the same kind of immutable update patterns that you use with ES6 Objects and Arrays:

```jsx
const initialState = #{
  user: #{
    firstName: "Sebastien",
    lastName: "Lorber"
  }
  company: #{
    name: "Lambda Scale",
  }
};


const updatedState = {
  ...initialState,
  company: {
    ...initialState.company,
    name: 'Freelance',
  },
};
```

So far, [ImmerJS](https://github.com/immerjs/immer) has won the immutable updates battle, due to its simplicity to handle nested attributes, and interoperability with regular JS code.

It is not clear how Immer could work with Records & Tuples yet, but it's something the proposal authors are exploring.

Michael Weststrate himself has [highlighted](https://twitter.com/mweststrate/status/1263482177934819329) that a [separate but related proposal](https://github.com/tc39/proposal-deep-path-properties-for-record) could **make ImmerJS unnecessary for Records & Tuples**:

```jsx
const initialState = #{
  counters: #[
    #{ name: "Counter 1", value: 1 },
    #{ name: "Counter 2", value: 0 },
    #{ name: "Counter 3", value: 123 },
  ],
  metadata: #{
    lastUpdate: 1584382969000,
  },
};

// Vanilla JS updates
// using deep-path-properties-for-record proposal
const updatedState = #{
  ...initialState,
  counters[0].value: 2,
  counters[1].value: 1,
  metadata.lastUpdate: 1584383011300,
};
```

### useMemo

In addition to memoizing expensive computations, `useMemo()` is also useful to **avoid creating new object identities**, that might **trigger useless computations, re-renders, or effects executions deeper in the tree**.

Let's consider the following use-case: you have an UI with multiple filters, and want to fetch some data from the backend.

Existing React code-bases might contain code such as:

```jsx
// Don't change apiFilters object identity,
// unless one of the filter changes
// Not doing this is likely to trigger a new fetch
// on each render
const apiFilters = useMemo(
  () => ({ userFilter, companyFilter }),
  [userFilter, companyFilter],
);

const { apiData, loading } = useApiData(apiFilters);
```

With Records & Tuples, this simply becomes:

```jsx
const {apiData,loading} = useApiData(#{ userFilter, companyFilter })
```

### useEffect

Let's continue with our api filters use-case:

```jsx
const apiFilters = { userFilter, companyFilter };

useEffect(() => {
  fetchApiData(apiFilters).then(setApiDataInState);
}, [apiFilters]);
```

Unfortunately, the fetch effect gets **re-executed**, because the identity of the `apiFilters` object changes every time this component re-renders. `setApiDataInState` will trigger a re-render, and you will end up with an infinite fetch/render loop.

This mistake is so common across React developers that there are thousand of Google search results for **[useEffect + "infinite loop"](https://www.google.com/search?q=useEffect+%2B+%22infinite+loop%22)**.

[Kent C Dodds](https://twitter.com/kentcdodds) even created a [tool](https://github.com/kentcdodds/stop-runaway-react-effects) to break infinite loops in development.

Very common solution: create `apiFilters` directly in the effect's callback:

```jsx
useEffect(() => {
  const apiFilters = { userFilter, companyFilter };
  fetchApiData(apiFilters).then(setApiDataInState);
}, [userFilter, companyFilter]);
```

Another creative solution (not very performant, found on [Twitter](https://twitter.com/acutmore/status/1256533631914426369)):

```jsx
const apiFiltersString = JSON.stringify({
  userFilter,
  companyFilter,
});

useEffect(() => {
  fetchApiData(JSON.parse(apiFiltersString)).then(
    setApiDataInState,
  );
}, [apiFiltersString]);
```

The one I like the most:

```jsx
// We already saw this somewhere, right? :p
const apiFilters = useMemo(
  () => ({ userFilter, companyFilter }),
  [userFilter, companyFilter],
);

useEffect(() => {
  fetchApiData(apiFilters).then(setApiDataInState);
}, [apiFilters]);
```

There are many fancy ways to solve this problem, but they all tend to **become annoying**, as the number of filters increase.

[use-deep-compare-effect](https://github.com/kentcdodds/use-deep-compare-effect) (from [Kent C Dodds](https://twitter.com/kentcdodds)) is likely the less annoying, but running deep equality on every re-render has a cost I'd prefer not pay.

They are much **more verbose and less idiomatic** than their Records & Tuples counterpart:

```jsx
const apiFilters = #{ userFilter, companyFilter };

useEffect(() => {
  fetchApiData(apiFilters).then(setApiDataInState);
}, [apiFilters]);
```

### Props and React.memo

Preserving object identities in props is also very useful for React performances.

Another very common performance mistake: create new objects identities in render.

```jsx
const Parent = () => {
  useRerenderEverySeconds();
  return (
    <ExpensiveChild
      // someData props object is created "on the fly"
      someData={{ attr1: 'abc', attr2: 'def' }}
    />
  );
};

const ExpensiveChild = React.memo(({ someData }) => {
  return <div>{expensiveRender(someData)}</div>;
});
```

Most of the time, this is not a problem, and React is fast enough.

But sometimes you are looking to optimize your app, and this new object creation makes the `React.memo()` useless. Worst, it actually **makes your application a little bit slower** (as it now has to run an additional shallow equality check, always returning false).

Another pattern I often see in client code-bases:

```jsx
const currentUser = { name: 'Sebastien' };
const currentCompany = { name: 'Lambda Scale' };

const AppProvider = () => {
  useRerenderEverySeconds();

  return (
    <MyAppContext.Provider
      // the value prop object is created "on the fly"
      value={{ currentUser, currentCompany }}
    />
  );
};
```

Despite the fact that `currentUser` or `currentCompany` **never gets updated**, your context value changes every time this provider re-renders, which trigger re-renders of all context subscribers.

All these issues can be solved with memoization:

```jsx
const someData = useMemo(
  () => ({ attr1: 'abc', attr2: 'def' }),
  [],
);

<ExpensiveChild someData={someData} />;
```

```jsx
const contextValue = useMemo(
  () => ({ currentUser, currentCompany }),
  [currentUser, currentCompany],
);

<MyAppContext.Provider value={contextValue} />;
```

With Records & Tuples, it is **idiomatic to write performant code**:

```jsx
<ExpensiveChild someData={#{ attr1: 'abc', attr2: 'def' }} />;
```

```jsx
<MyAppContext.Provider value={#{ currentUser, currentCompany }} />;
```

### Fetching and re-fetching

There are many ways to fetch data in React: `useEffect`, HOC, Render props, Redux, SWR, React-Query, Apollo, Relay, Urql, ...

Most often, we hit the backend with a request, and get some JSON data back.

To illustrate this section, I will use [react-async-hook](https://github.com/slorber/react-async-hook), my own very simple fetching library, but this applies to other libraries as well.

Let's consider a classic async function to get some API data:

```jsx
const fetchUserAndCompany = async () => {
  const response = await fetch(
    `https://myBackend.com/userAndCompany`,
  );
  return response.json();
};
```

This app fetches the data, and ensure this data stays "fresh" (non-stale) over time:

```jsx
const App = ({ id }) => {
  const { result, refetch } = useAsync(
    fetchUserAndCompany,
    [],
  );

  // We try very hard to not display stale data to the user!
  useInterval(refetch, 10000);
  useOnReconnect(refetch);
  useOnNavigate(refetch);

  if (!result) {
    return null;
  }

  return (
    <div>
      <User user={result.user} />
      <Company company={result.company} />
    </div>
  );
};

const User = React.memo(({ user }) => {
  return <div>{user.name}</div>;
});

const Company = React.memo(({ company }) => {
  return <div>{company.name}</div>;
});
```

Problem: you have used `React.memo` for performance reasons, but every time the re-fetch happens, you end up with a new JS object, with a **new identity**, and **everything re-renders**, despite the fetched data being the **same as before** (deeply equal payloads).

Let's imagine this scenario:

- you use the "Stale-While-Revalidate" pattern (show cached/stale data first, then refresh data in the background)
- your page is complex, render intensive, with lots of backend data being displayed

You navigate to a page, that is already expensive to render the first time (with cached data). One second later, the refreshed data comes back. Despite being deeply equal to the cached data, everything re-renders again. Without [Concurrent Mode](https://reactjs.org/docs/concurrent-mode-intro.html) and time slicing, some users may even notice **their UI freezing** for a few hundred milliseconds.

Now, let's convert the fetch function to return a Record instead:

```jsx
const fetchUserAndCompany = async () => {
  const response = await fetch(
    `https://myBackend.com/userAndCompany`,
  );
  return JSON.parseImmutable(await response.text());
};
```

By chance, JSON is compatible with Records & Tuples, and you should be able to convert any backend response to a Record, with [JSON.parseImmutable](https://github.com/tc39/proposal-record-tuple#jsonparseimmutable).

**Note**: [Robin Ricard](https://twitter.com/r_ricard), one of the proposal authors, is pushing for a new **`response.immutableJson()`** function.

With Records & Tuples, if the backend returns the same data, you **don't re-render anything** at all!

Also, if only one part of the response has changed, the other nested objects of the response will still **keep their identity**. This means that if only `user.name` changed, the `User` component will re-render, but not the `Company` component!

I let you imagine the performance impact of all this, considering patterns like "Stale-While-Revalidate" are becoming increasingly popular, and provided out of the box by libraries such as SWR, React-Query, Apollo, Relay...

## Reading query strings

In search UIs, it's a good practice to **preserve the state of filters in the querystring**. The user can then copy/paste the link to someone else, refresh the page, or bookmark it.

If you have 1 or 2 filters, that's simple, but as soon as your search UI becomes complex (10+ filters, ability to compose queries with AND/OR logic...), you'd better use a good abstraction to manage your querystring.

I personally like [qs](https://github.com/ljharb/qs): it's one of the few libraries that handle nested objects.

```jsx
const queryStringObject = {
  filters: {
    userName: 'Sebastien',
  },
  displayMode: 'list',
};

const queryString = qs.stringify(queryStringObject);

const queryStringObject2 = qs.parse(queryString);

assert.deepEqual(queryStringObject, queryStringObject2);

assert(queryStringObject !== queryStringObject2);
```

`queryStringObject` and `queryStringObject2` are deeply equal, but they have not the same identity anymore, because `qs.parse` creates new objects.

You can integrate the querystring parsing in a hook, and "stabilize" the querystring object with `useMemo()`, or a lib such as [`use-memo-value`](https://github.com/discord/use-memo-value).

```jsx
const useQueryStringObject = () => {
  // Provided by your routing library, like React-Router
  const { search } = useLocation();
  return useMemo(() => qs.parse(search), [search]);
};
```

Now, imagine that deeper in the tree you have:

```js
const { filters } = useQueryStringObject();

useEffect(() => {
  fetchUsers(filters).then(setUsers);
}, [filters]);
```

This is a bit nasty here, but the same problem happens again and again.

Despite the usage of `useMemo()`, as an attempt to preserve `queryStringObject` identity, you will end up with unwanted `fetchUsers` calls.

When the user will update the `displayMode` (that should only change the rendering logic, not trigger a re-fetch), the querystring will change, leading to the querystring being parsed again, leading to a new object identity for the `filter` attribute, leading to the unwanted `useEffect` execution.

Again, Records & Tuples would prevent such things to happen.

```jsx
// This is a non-performant, but working solution.
// Lib authors should provide a method such as qs.parseRecord(search)
const parseQueryStringAsRecord = (search) => {
  const queryStringObject = qs.parse(search);

  // Note: the Record(obj) conversion function is not recursive
  // There's a recursive conversion method here:
  // https://tc39.es/proposal-record-tuple/cookbook/index.html
  return JSON.parseImmutable(
    JSON.stringify(queryStringObject),
  );
};

const useQueryStringRecord = () => {
  const { search } = useLocation();
  return useMemo(() => parseQueryStringAsRecord(search), [
    search,
  ]);
};
```

Now, even if the user updates the `displayMode`, the `filters` object will preserve its identity, and not trigger any useless re-fetch.

**Note**: if the Records & Tuples proposal is accepted, libraries such as `qs` will likely provide a `qs.parseRecord(search)` method.

### Deeply equal JS transformations

Imagine the following JS transformation in a component:

```jsx
const AllUsers = [
  { id: 1, name: 'Sebastien' },
  { id: 2, name: 'John' },
];

const Parent = () => {
  const userIdsToHide = useUserIdsToHide();

  const users = AllUsers.filter(
    (user) => !userIdsToHide.includes(user.id),
  );

  return <UserList users={users} />;
};

const UserList = React.memo(({ users }) => (
  <ul>
    {users.map((user) => (
      <li key={user.id}>{user.name}</li>
    ))}
  </ul>
));
```

Every time the `Parent` component re-renders, the `UserList` component re-render as well, because `filter` will always return a **new array instance**.

This is the case even if `userIdsToHide` is empty, and `AllUsers` identity being stable! In such case, the filter operation does not actually filter anything, it just **creates new useless array instances**, opting out of our `React.memo` optimisations.

These kind of transformations are very common in React codebase, with operators such as `map` or `filter`, in components, reducers, selectors, Redux...

Memoization can solve this, but it's more idiomatic with Records & Tuples:

```jsx
const AllUsers = #[
  #{ id: 1, name: 'Sebastien' },
  #{ id: 2, name: 'John' },
];

const filteredUsers = AllUsers.filter(() => true);

AllUsers === filteredUsers;
// true
```

### Records as React key

Let's imagine you have a list of items to render:

```jsx
const list = [
  { country: 'FR', localPhoneNumber: '111111' },
  { country: 'FR', localPhoneNumber: '222222' },
  { country: 'US', localPhoneNumber: '111111' },
];
```

What key would you use?

Considering both the `country` and `localPhoneNumber` are not **independently unique** in the list, you have 2 possible choices.

**Array index key**:

```jsx
<>
  {list.map((item, index) => (
    <Item key={`poormans_key_${index}`} item={item} />
  ))}
</>
```

This always works, but is far from ideal, particularly if the items in the **list are reordered**.

**Composite key**:

```jsx
<>
  {list.map((item) => (
    <Item
      key={`${item.country}_${item.localPhoneNumber}`}
      item={item}
    />
  ))}
</>
```

This solution handle better the **list re-orderings**, but is only possible if we know for sure that the **couple / tuple is unique**.

In such case, wouldn't it be **more convenient** to use **Records as the key** directly?

```jsx
const list = #[
  #{ country: 'FR', localPhoneNumber: '111111' },
  #{ country: 'FR', localPhoneNumber: '222222' },
  #{ country: 'US', localPhoneNumber: '111111' },
];

<>
  {list.map((item) => (
    <Item key={item} item={item} />
  ))}
</>
```

This was [suggested](https://twitter.com/barklund/status/1289273309889064960) by [Morten Barklund](https://twitter.com/barklund).

### Explicit API surface

Let's consider this TypeScript component:

```jsx
const UsersPageContent = ({
  usersFilters,
}: {
  usersFilters: UsersFilters,
}) => {
  const [users, setUsers] = useState([]);

  // poor-man's fetch
  useEffect(() => {
    fetchUsers(usersFilters).then(setUsers);
  }, [usersFilters]);

  return <Users users={users} />;
};
```

This code may or may not create an infinite loop, as we have seen already, depending on how stable the usersFilters prop is. This creates an implicit API contract that should be documented and clearly understood by the implementor of the parent component, and despite using TypeScript, this is not reflected in the type-system.

The following will lead to an infinite loop, but TypeScript has no way to prevent it:

```jsx
<UsersPageContent
  usersFilters={{ nameFilter, ageFilter }}
/>
```

With Records & Tuples, we can tell TypeScript to expect a Record:

```jsx
const UsersPageContent = ({
  usersFilters,
}: {
  usersFilters: #{nameFilter: string, ageFilter: string}
}) => {
  const [users, setUsers] = useState([]);

  // poor-man's fetch
  useEffect(() => {
    fetchUsers(usersFilters).then(setUsers);
  }, [usersFilters]);

  return <Users users={users} />;
};
```

**Note**: the `#{nameFilter: string, ageFilter: string}` is my own invention: we don't know yet what will be the TypeScript syntax.

TypeScript compilation will fail for:

```jsx
<UsersPageContent
  usersFilters={{ nameFilter, ageFilter }}
/>
```

While TypeScript would accept:

```jsx
<UsersPageContent
  usersFilters={#{ nameFilter, ageFilter }}
/>
```

With Records & Tuples, we can prevent this infinite loop at **compile time**.

We have an **explicit** way to tell the compiler that our **implementation is object-identity sensitive** (or relies on by-value comparisons).

**Note**: `readonly` would not solve this: it only prevents mutation, but does not guarantee a stable identity.

### Serialization guarantee

You may want to ensure that developers on your team don't put unserializable things in global app state. This is important if you plan to send the state to the backend, or persist it locally in `localStorage` (or `AsyncStorage` for React-Native users).

To ensure that, you just need to ensure that the root object is a record. This will guarantee that all the nested attributes are also primitives, including nested records and tuples.

Here's an example integration with Redux, to ensure the Redux store keeps being serializable over time:

```jsx
if (process.env.NODE_ENV === 'development') {
  ReduxStore.subscribe(() => {
    if (typeof ReduxStore.getState() !== 'record') {
      throw new Error(
        "Don't put non-serializable things in the Redux store! " +
          'The root Redux state must be a record!',
      );
    }
  });
}
```

**Note**: this is not a perfect guarantee, because `Symbol` can be put in a Record, and is not serializable.

### CSS-in-JS performances

Let's consider some CSS-in-JS from a popular lib, using the css prop:

```jsx
const Component = () => (
  <div
    css={{
      backgroundColor: 'hotpink',
    }}
  >
    This has a hotpink background.
  </div>
);
```

Your CSS-in-JS library receives a new CSS object on every re-render.

On first render, it will hash this object as a unique class name, and insert the CSS.
The style object has a different identity for each re-render, and the CSS-in-JS library have to **hash it again and again**.

```jsx
const insertedClassNames = new Set();

function handleStyleObject(styleObject) {
  // computeStyleHash re-executes every time
  const className = computeStyleHash(styleObject);

  // only insert the css for this className once
  if (!insertedClassNames.has(className)) {
    insertCSS(className, styleObject);
    insertedClassNames.add(className);
  }

  return className;
}
```

With Records & Tuples, the identity of such a style object is preserved over time.

```jsx
const Component = () => (
  <div
    css={#{
      backgroundColor: 'hotpink',
    }}
  >
    This has a hotpink background.
  </div>
);
```

Records & Tuples can be used as [Map keys](https://github.com/tc39/proposal-record-tuple#usage-in-mapsetweakmapweakset). This could make the implementation of your CSS-in-JS library faster:

```jsx
const insertedStyleRecords = new Map();

function handleStyleRecord(styleRecord) {
  let className = insertedStyleRecords.get(styleRecord);

  if (!className) {
    // computeStyleHash is only executed once!
    className = computeStyleHash(styleRecord);
    insertCSS(className, styleRecord);
    insertedStyleRecords.add(styleRecord, className);
  }

  return className;
}
```

We don't know yet about Records & Tuples [performances](https://github.com/tc39/proposal-record-tuple#what-are-the-performance-expectations-of-these-data-structures) (this will depend on browser vendor implementations), but I think it's safe to say it will be faster than creating the equivalent object, and then hashing it to a className.

**Note**: some CSS-in-JS library with a good Babel plugin might be able to transform static style objects as constants at compilation time, but they will have a hard time doing so with dynamic styles.

```jsx
const staticStyleObject = { backgroundColor: 'hotpink' };

const Component = () => (
  <div css={staticStyleObject}>
    This has a hotpink background.
  </div>
);
```

---

# Conclusion

Many React performance and behavior issues are related to object identities.

**Records & Tuples** will ensure that object identities are **"more stable"** out of the box, by providing some kind of **"automatic memoization"**, and help us solve these React problems more easily.

Using TypeScript, we may be able to express better that your API surface is **object-identity sensitive**.

I hope you are now as excited as I am by this proposal!

Thank you for reading!

Thanks [Robin Ricard](https://twitter.com/r_ricard), [Rick Button](https://twitter.com/rickbutton), [Daniel Ehrenberg](https://twitter.com/littledan), [Nicolò Ribaudo](https://twitter.com/NicoloRibaudo), [Rob Palmer](https://twitter.com/robpalmer2) for their work on this awesome proposal, and for reviewing my article.

---

If you like it, spread the word with a [Retweet](https://twitter.com/sebastienlorber/status/1289222799882481665), [Reddit](https://www.reddit.com/r/reactjs/comments/i19y22/records_tuples_for_react/) or [HackerNews](https://news.ycombinator.com/item?id=24010282).

Browser code demos, or correct my post typos [on the blog repo](https://github.com/slorber/sebastienlorber.com/tree/master/content/posts/2020-07-31-records-and-tuples-for-react-developers)

For more content like this, subscribe to [my mailing list](https://mailchi.mp/4ea4df0b54f7/sebastienlorber) and follow me on [Twitter](https://twitter.com/sebastienlorber).