Generating sequential IDs for ReactJS keys - reactjs

React requires that you have a unique key attribute on elements in an array. The React docs recommend using an id from your data. If you don't have an id then you can use the array index as a "last resort", with the caveat that using an index can cause performance issues when the items can be re-ordered. I've also had some annoying issues when building an array of elements from multiple source arrays and multiple calls to .map(), each with different transformations, in which case the array indexes won't be unique anyways.
I've started throwing the following boilerplate into my components:
constructor(props) {
super(props);
this.getId = this.getId.bind(this);
this.id = 0;
}
getId() {
return this.id++;
}
which can be used like so:
render() {
const items = this.props.greetings.map(text => ({
<li key={this.getId()}>{text}</li>
}));
return (
<ul>
{items}
</ul>
);
}
This is simple, fast, and doesn't have the issues that using the array index has, but I don't know if there are any issue with this approach or better alternatives that I'm overlooking. Is this bad practice? Are there any other simple ways of generating keys that don't have the array index issues?

This is what the docs on Reconciliation have to say on keys
In practice, finding a key is usually not hard. The element you are going to display may already have a unique ID, so the key can just come from your data.
When that’s not the case, you can add a new ID property to your model or hash some parts of the content to generate a key. The key only has to be unique among its siblings, not globally unique.
As a last resort, you can pass item’s index in the array as a key. This can work well if the items are never reordered, but reorders will be slow.
So I think you are following the suggested approach.

Related

Typescript - getting map keys by its values

I have a Map<string, boolean>. I want to extract the keys to an array based on their values. I want to end up with two string arrays; one being keys whose values are false and one being keys whose values are true.
I tried to filter them like this:
const trueKeys = [...myMap.entries()].filter(it => it[1] === true).map(it => it[0]);
const falseKeys = [...myMap.entries()].filter(it => it[1] === false).map(it => it[0]);
But it requires iterating over the entries twice for each array. Is there a better way to achieve this?
I don't think iterating over a map's entries twice vs once is a big deal, as it's hard for me to imagine a situation where changing that would make the difference between code that performs poorly and code that performs well (like running away from a tsunami; you're either too close or far enough away, and running is almost certainly not changing that).
Still, if you want to traverse only once, you can do so:
const trueKeys: string[] = [];
const falseKeys: string[] = [];
myMap.forEach((v, k) => (v ? trueKeys : falseKeys).push(k));
console.log(trueKeys, falseKeys);
Here I'm iterating the Map just once, with the forEach() method. Even [...myMap.entries()] iterates the array once before you even try to filter it, so I've avoided this to ensure that we're only traversing once.
And in the callback I push() each key into one of two arrays depending on the value. This mutates the output arrays, and you can rewrite not to do so, but then you'd almost certainly be throwing away the modest amount of performance gained.
Playground link to code

Why should I use key as string for list item in React?

React Doc explains why giving a key to list item is important. It specifies that a key is string attribute.
So should I convert my id to string for key every time?
<ul>
{data.map(item => (
<li key={item.id.toString()}>
{item.text}
</li>
</ul>
Could you tell me the reason for this? I thought about problem with number sort as strings but it seemed to be another case.
React Doc. List and Keys
Key is fundamental for the React to index a list in the render process (in the background). If you don't put the key, you get this:
This simply means that he needs to look for the component, what will take longer.
To solve the toString(), to this:
key={`OperationTesterInput_${idx}`
Looks much more cleaner. You can also use the index parameter present in the map function, also does the trick :)
The Reason why its a string
It is not. The reason is because of typescript. The implementation is the following:
So following this implementation, this is valid:
Generally, Key is used so that if an element changes or is removed, React only needs to rerender that specific element rather than the whole list. This key is required to be a string because the HTML attribute values are always strings, whatever value you give must be serialized to a string. We can assign anything to an attribute, but it becomes a string.

ReactJS - Is it possible to render this dictionary (via map) when the keys can change?

Question Is it possible to render a dictionary with a key (which isn't known until an algorithm is run) with a value that is an array, itself with a dictionary with unknown key-value pairs until an algorithm is run?
Detailed information
I have this dictionary:
var currentWorkers = = {EmployeesAtRestaurant :
[{"James" : "Manager"},
{"Jessica" : "Waiter"},
{"Bob" : "Waiter"},
{"Ben" : "Chef"}],
EmployeesAtOffice :
[{"Rebecca" : "Manager"},
{"Nicole" : "Part-time Employee"},
{"Robert" : "Full-time Employee"},
{"Eric" : "Full-time Employee"}],
EmployeesAtZoo :
[{"Robert" : "Manager"},
{"Naomi" : "Part-time Employee"},
{"Jennifer" : "Full-time Employee"},
{"Ken" : "Full-time Employee"}]}
And I want to render it on a page as below (mock up). It is to display employees of an organisation:
What I've tried
I've read some previous answers on Stack (Push component to array of components - ReactJS) of how to attempt this, but their dictionaries use a simple key and value pair, and since my key is not known (i.e I can't do dictionary.Organisation for example) I'm not able to do the above.
I've tried to remodel the dictionary into a model similar to the above, but then I lose a lot of the information above.
Frankly, I'm beginning to suspect my best option is to just remodel the dictionary at this point, if the above is too difficult to attempt.
To make sure I'm understanding your question: Are you talking about the special prop called key[1] that React uses for rendering?
If that's the case, the important thing is that each key is unique, but it doesn't necessarily have to be the same key that your algorithms are calculating.
If you don't have access to the results of your algorithm yet, but you still want to render the strings like in your screenshots, you'll need a different unique key to use while mapping.
The map function on Arrays sends the element as the first function parameter, and the element's index as the second parameter[2]. Lots of places will warn you that index keys aren't the best. As far as I know, this is because if the order of the Array shifts then you lose the optimization that React is trying to provide you.
If index is the only unique data you've got, however, it's reasonable to consider using it. This is especially true if the data is coming from a static source, because you know that the order of the data isn't going to shift out from under you.
Let me know if I've misunderstood your question.
https://reactjs.org/docs/lists-and-keys.html#keys
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Is it OK to use unique, stable functions (e.g. components) rather than strings as React keys?

When answering this question I ended up using an index as a key in the following code:
const menuItems = [
{ Page: ActiveDeals, Icon: Home },
{ Page: UpcomingDates, Icon: CalendarToday },
{ Page: DealsPipeline, Icon: FilterList }
];
const mappedMenuItems = menuItems.map((menuItem, index) => (
<DashboardListItem
key={index}
{...menuItem}
ShownPage={this.state.shownPage}
setShownPage={this.setShownPage}
/>
));
This was fine in this case since the order isn't volatile, but ideally I would have used something specific to the data for the unique key. In this case, I didn't have (or need) any string identifiers in the data. The main unique thing about each entry in this array was the content component passed via the Page property to each menuItem. This function would be unique to each menu item and would be stable over time (not change with re-renders) so it fits most of the criteria for the key property when rendering a list/array of components, but the documentation indicates strings as the preferred key type.
So I wondered, "Is it OK to use a function as a key?"
I've included my own answer at the same time as posting the question, but feel free to answer as well if you have something to add -- particularly if you know of specific reasons for why the React team recommends strings as keys.
To answer this question I first looked in the docs which definitely strongly suggest that keys be strings.
From https://reactjs.org/docs/lists-and-keys.html#basic-list-component
A “key” is a special string attribute you need to include when
creating lists of elements.
and also in https://reactjs.org/docs/lists-and-keys.html#keys
The best way to pick a key is to use a string that uniquely identifies
a list item among its siblings. When you don’t have stable IDs for
rendered items, you may use the item index as a key as a last resort
So far the docs mention strings and integers (e.g. index), but I still wondered whether there was any harm in using something else. So the next place to look was the source code.
To find the correct source code, I left off a key where I should have one and then used the resulting message ("Each child in a list should have a unique...") to search the GitHub repository. This brought me to ReactChildFiber.js. I then found the following function relevant to my question:
function mapRemainingChildren(
returnFiber: Fiber,
currentFirstChild: Fiber,
): Map<string | number, Fiber> {
// Add the remaining children to a temporary map so that we can find them by
// keys quickly. Implicit (null) keys get added to this set with their index
// instead.
const existingChildren: Map<string | number, Fiber> = new Map();
let existingChild = currentFirstChild;
while (existingChild !== null) {
if (existingChild.key !== null) {
existingChildren.set(existingChild.key, existingChild);
} else {
existingChildren.set(existingChild.index, existingChild);
}
existingChild = existingChild.sibling;
}
return existingChildren;
}
So here existingChildren is a Map that the keys are used in. Map can support functions as keys, and I tried out using menuItem.Page as my key and it seems to work fine, but the types in the code clearly indicate string | number as the type of the key.
For now my conclusion is that types other than string | number should be avoided since the source declares this as a restriction and that there may be reasons I am unaware of (possibly regarding future plans) that may make this restriction more important than it currently seems to be, but I'm still curious if there are stronger reasons to restrain the types used for keys.

What are the valid symbols for a React 'key'

What are the valid symbols for a react key prop as such;
<div key="can i use spaces for example?"></div>
In my case I want to use a URL as the key
const links = [
{
label: 'foo label',
href: 'https://example.com/foo',
},
{
label: 'bar label',
href: 'https://example.com/bar',
}
]
links.map(
link => (<a key={link.href} href={link.href}>{link.label}</a>)
);
Is this valid? I was thinking that I could use some hash function to pass the href through first, but this is a pointless step if any character is guaranteed to be valid in a key value.
The reason I'm asking is that I can't find any example in the doc that uses a non-alpha-numeric character for the key, and also explicitly says that, if you don't have an ID to use as key for the object you're rendering you can hash some part of it to make a key. Although this could be because you shouldn't use very long keys, and should therefor hash the content first to truncate it's size, it seems that the whole documentation implicitly says that only alpha-numeric characters should be used as a key.
Requirements for React's key is best described in the documentation for reconciliation
In practice, finding a key is not really hard. Most of the time, the
element you are going to display already has a unique id. When that's
not the case, you can add a new ID property to your model or hash some
parts of the content to generate a key. Remember that the key only has
to be unique among its siblings, not globally unique.
So, the key should be unique (amongst its siblings) and stable.
Your example, therefore, is a good fit. Spaces should be fine as well.

Resources