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

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.

Related

How do I access value of a selected <option> tag down 2 trees in a function?

PizzaBuilder1.js
Ingredients.js
Bases.js
PizzaBuilder2.js
PizzaBuilder3.js
I'll try to describe my issue in the least confusing way. So what I'm trying to achieve is create a function(pic4-PizzaBuilder2), where I compare the given key of this.state.ingredients.Bases (and other sub-objects, but that's not important for now) with value of selected option tag in Bases.js and when it matches, set the given value of that key to true. The hierarchy is like this: PizzaBuilder.js > Ingredients.js > Bases.js. I have already managed to match each option value with the key inside the object with the same name. However, what I'm still struggling with is how to access it in PizzaBuilder.js when the select option value I wanna compare it to is 2 trees down (see pic4-PizzaBuilder2, that is supposed to be const selectedOption, but I obviously still need to figure out how to access it). I'll be really grateful for any kind of advice.

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.

React Native: FlatList keyExtractor & toString() Issue?

Noob question with React-Native & FlatLists:
What does the 2nd line actually mean ?
data={this.state.data}
keyExtractor={(x,i)=>i}
I borrowed this line of code from a friend (with his permission) and it's kinda gotten me into a warning issue with react-native.
I keep getting a yellow warning telling me that there's a "failed child context type". Which is react-native code for "I'm sorry dear programmer, but I can't find the key field to sort the data you presented me with! Noob! Pfft!". I get that. And the way to solve it is to add .toString() to the field that's denoted as the key Field. The problem is, as you can see, is that's not a key field there but rather > (x,i)=>i.
So my question is WTH is (x,i)=>i ?
Barring that, I tried to use what I thought was the id value of the record that I'm pulling: this.state.data.id, as the flatlist sees that datasource as denoted in this.state.data, and it sees it as an array as most you already know. My second question is why can't I use 'this.state.data.id' as the keyfield ?
And if I can't use this.state.data.id as the keyfield, and using the (x,i)=>i thingy (whatever the frak that thing is) and I have to use it, what is it and how would I add .toString() to it ?
Thanks ahead of time.
My apologies for the Noob question.
Have a nice day!
(x,i)=>i is actually a callback function where x=item and i=index, so you return index as a key. Consider it like:
keyExtractor={(item,index)=>index}
Since it wants key to be a String, you can use:
keyExtractor={(item,index)=>index.toString()}
Apparently you already know what the key prop is for so I won't invest too much time on it. (If in doubt check the docs).
So let's answer in parts. (x, i) => i is an ES6 lambda function. The keyExtractor is a function that receives as the first argument the item from data (in this callback it was called x) and the second argument is the element index in data array (in this case called i). So the code says "use as key for my element the index in the array".
The keyExtractor is necessary because FlatList encapsulates the return value of your renderItem and needs a key for the list. (I can go deeper here if you like).
Note that keyExtractor provides keys for each element and not for the list itself, so using this.state.data.id would put the same key in all element, which is not what you want.
Finally the .toString() part is just because the key type expected by FlatList is a string, so the warning.

React Collapse transition doesn't animate when wrapped in a div with a dynamic key

This is really weird. It took me many hours to figure out how to fix this. But even with it fixed, I don't know why it breaks in one configuration but not the other.
The best way to explain this is with the StackBlitz live example: https://stackblitz.com/edit/react-collapse-transition-breaks-with-dynamic-key
The live example details the exact issue, but here's the synopsis:
It's a React app using Material UI.
I have a <List> that is populated with an array of <ListItem>s.
Those <ListItem>s are clickable. When you click them, it uses the <Collapse> transition to expose a sub-<List> of "subheaders". The subheader <List>s are also populated with an array of <ListItem>s.
Here's where it gets weird: I have a simple <div> that holds the "header" <ListItem>s and the <Collapse>-ible <List> of "subheaders".
Because these are part of an array, React complains if I don't add a "key" attribute to top-level element (the <div>).
If I add a dynamically-generated key value to that containing <div>, it somehow kills the transition animation on the <Collapse> element. The <Collapse> still opens-and-closes, but it doesn't animate over a set number of milliseconds. It just opens (immediately) or closes (immediately).
If I add a static key value to that same containing <div>, the animation works just fine.
Why does the dynamic-key approach break the animation??
TLDR: Do not dynamically-generate globally-unique keys for array elements in React.
OK, after repeated attempts at Googling, I think I finally understand what's going on.
When you add items to an array in React, a warning is thrown if you don't add a unique "key" to each element in the array. As long as you use something unique as the "key" value for each element, the warning goes away and, for the most part, React seems to manage the array elements just fine.
When I was starting React development (a few years ago), I thought, "I can solve this easily by using a random GUID-generating function to add unique keys to all my array elements. So I would frequently use code that looks like this:
let newArray = [];
someMasterArrayOfObjects.forEach(object => {
if (someConditionIsMet) {
// SEE HOW CLEVER I THOUGHT I WAS?? USING A RANDOMLY-GENERATED GUID QUIETS THE
// UNIQUE-KEY WARNINGS THAT ARE THROWN BY REACT
newArray.push(
<div key={createRandomGuid()}>
{object.title}
</div>
);
}
});
But I missed one key word from the ReactJS documentation (emphasis, mine):
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a STABLE identity:
They use the word "stable" a few times in the documentation, but it didn't really sink into my thick skull. Like most devs, my first concern was removing the warning that is thrown when array elements are missing a unique key. And using a randomly-generated GUID solved that problem.
But React uses those keys to figure out what should be re-rendered during state changes. If you randomly-generate a new, globally-unique key every time you build the elements in the array, then React will assume that all of these elements need to be completely rebuilt from scratch every time you set state.
At the very least, this is inefficient. You may not notice any performance effects in small arrays/apps, but there's no reason to artificially force a re-rendering of every array element every time that state is set for any reason. You may not notice any visual problem in your app, but it's poor practice.
At the worst, it will actually break some of your functionality. In my case, it was breaking the <Collapse> transition animation because the rendering engine couldn't compare the incremented "height" value between one state change and the next - because on every attempt to change that height, I was assigning a brand new, globally-unique identifier to the "key" value and React was seeing it as an entirely new, entirely different component.
Once I finally figured out what to Google, I also found this great JSFiddle:
http://jsfiddle.net/frosas/S4Dju/
Notice that the center row of inputs in his example are titled "Unique random keys". When you manually change the value of one of the those unique random keys, then you click on "Add item", it blows away the results of your changes, because it re-renders the input elements as brand-new elements with no ties to their previous state.

Resources