0

That's obvious that for the diffing algorithm in React keys are essential. But I was wandering, why React just can't automatically generate keys based on the content we iterate over?

I also assume that items can share some similarity, or cab be identical in terms of content, but isn't it possible to generate keys once user open a page and somehow attach them to the items, so it is stable?

Or maybe there where attempts to solve the problem, if so, I would be grateful if you share it to me.

Update

Thank you guys for your answers, I've learnt a lot! Also a thing I had in mind: what we developers do when there is no stable id (e.g. user added an item which is not yet saved into DB). In the cases we just generate id, and attach it to the object, or element in an array, but we do not generate ids on a fly, so it remains stable over time.

What if React just generate ids for all arrays which are involved into rendering process, in other words, arrays which are directly used in render function?

It can be done only once, during phase Commit phase, or whatever. Also I believe, the id can be readonly, or something, so user can't erase the id.

p.s.s While I was writing p.s. question above, I realized, autogenerating id for arrays wouldn't work, since I've missed two things. All side effect react can do only during the Commit phase, but not Render phase. But that's not the main problem.

The main problem is when we use filtering or sorting on a back-end side. Since we receive a new array, filtered one, we would need to regenerate ids for those elements, but basically, that's the same html elements, in which we can change content to match filtering order. That's the same as Slava Knyazev mentioned.

halfer
  • 19,824
  • 17
  • 99
  • 186
Andrik Dizhak
  • 95
  • 1
  • 5

3 Answers3

0

I believe, unless your data is 100% certainly going to sort in one way and never change, key={index} isn't a good key (which is what I assume you want your auto-generated keys to be). You'd ideally want something that is unique to each item, regardless of the order.

It's explained in more detail in the new beta react docs https://beta.reactjs.org/learn/rendering-lists#where-to-get-your-key

  • I think OP is not saying React could use the index automatically, but is asking why React cant use the objects full representation as the key by using hashing for example. This would work in most cases, but not all. – adsy Oct 01 '22 at 21:59
0

React can't generate keys, because the entire point of keys is for you to help React track elements during it's tree-diffing stage.

For example, lets say you have the following code, where you naively use content instead of identifiers for your keys:

const people = usePeople(); // [{ id: "1", name: "James"}, {id: "2", name: "William"}]
return <ul>{people.map(p => <li key={p.name}>{p.name}</li>}</ul>

The above code will function and behave as you would expect. But what happens if the name of a person changes? To understand it, lets look at the tree it generates:

ul
  li(James) James
  li(William) William

If James becomes Josh between renders, the new tree will look like this:

ul
  li(Josh) Josh
  li(William) William

React will compare the two results and conclude the following:

  • li(James) is to be removed
  • li(Josh) is to be added

However, if we set our key prop to p.id, then the old and new tree will look as follows, respectively:

ul
  li(1) James
  li(2) William
ul
  li(1) Josh
  li(2) William

And when React compares the two, it will identify that James has become Josh, and needs only the text adjusted.

In the first scenario, the <li> component is completely destroyed, and a completely new component takes its place. Both of these actions run a complete React lifecycle for the component. In the second, it remains untouched, and only the text inside changes.

While in this contrived scenario, the performance penalty in the first case in minimal, it may be very significant with complex components.

Slava Knyazev
  • 5,377
  • 1
  • 22
  • 43
  • In many case, it could create a unique and stable object key by hashing the object in a generic way with no developer provided key getter. It took me a while to understand what OP was asking though. See my answer. – adsy Oct 01 '22 at 22:01
-1

I think what you are implying is React could potentially choose to use something like a stable hash (say sha1 on a serialised string or something) on the object to generate a unique key. I think this actually would work in many cases, and even gave me pause for thought for a while! Your question is actually a really good one, and a deep one.

However, it wouldn't work in every case. I think it would work only on a static object which has no methods or anything attached. On a JS object, not all properties are enumerable. Hashing something could only ever happen on the enumerable objects of properties, but the dev may have non-enumerable yet-still-unique methods attached to these objects. In fact, even enumerable methods cant really be serialised reliably and they could be what makes the object unique. Not to mention the complexities of reliably hashing something with prototypical inheritance involved.

I suspect there's also a performance aspect to this. Hashing is cheap, but no that cheap. Most cases can be keyed by just referencing a unique ID in the object, which is vastly cheaper. When enumerating a very large number of objects, these things matter, and so its better to defer to the developer. After all, if you really do want to hash it, its just one function call in userland -- and this saves great confusion on developer side when it doesn't work. The Principle of least astonishment comes to mind.

There's also an aspect of how this would limit the power of how expressive JSX can be due to it basically allowing free-form JS. You would probably have to supply some low level <React.Map> component primitives in order for react to supply this default key handling which implies you are a bit more restrained on what you can and can't do (complex functional chains).

halfer
  • 19,824
  • 17
  • 99
  • 186
adsy
  • 8,531
  • 2
  • 20
  • 31
  • This answer goes off on a false premise that React is somehow aware of the data it maps to JSX -- it is not. The purpose of `key` to track components with changing data across renders in a way that would be impossible. `key` are not a burden place upon developers, they are a performance optimization. – Slava Knyazev Oct 01 '22 at 22:04
  • Did you read the last paragraph? Yes, it would need to become aware, and thats one strong reason they dont do it now. Its a question about why they made the choices they did. Of course you are correct about the current reality. – adsy Oct 01 '22 at 22:04
  • It would not be impossible if React provided loop primitives. Its not a good idea though, hence the answer. – adsy Oct 01 '22 at 22:06
  • I also addressed exactly the performance problem in my answer. Please reread it. – adsy Oct 01 '22 at 22:07
  • None of your answer addresses how React actually works or what keys do, which is what the question is about. – Slava Knyazev Oct 01 '22 at 22:07
  • I know perfectly how it works and what keys do. The OP is asking why it works how it does and why React authors did not choose to attempt to automatically use a hash or generic uniqueness function and alter the api drastically to fit. Its a bad idea, and you are actually agreeing with me here... – adsy Oct 01 '22 at 22:09