How does react key works - reactjs

I read this article about 'why using index as a key is an anti pattern' and got curious why my project works so well with index based key matrix.
I worked on tetris project, and every second, matrix with 20 * 24 cells re-render and move tetromino. Each cell of the array includes the alphabet and it becomes division's className and decide the color of the cell and every second when tetromino moves, it works very well.
From his article's example and this question, if key doesn't get changed, react doesn't change that DOM element and add new DOM for a new item in the array. So my tetris stage should not update view because key doesn't get changed. I got so confused how react key works.
I searched a lot of articles, but what I could find only was that they are using for optimization and compare DOM, but not how. So I want to know, how does key works and what kind of procedure they do to compare before change Virtual Dom and after change Virtual DOM

React on its own when not using 'key' attribute is not very efficient when comparing lists, if an item at the beginning of the list is changed or inserted.
If you insert an element at the beginning of your list, React will rerender the whole list and not just add the inserted item (if you have no key attribute set).
Same applies if you use an index based key, as the key of all items will change on insertion and therefore the whole list will rerender.
With a correctly set key attribute react is able detect which item was inserted or changed and only rerender this part of the list.
You can find a detailed example and explanation in the React Docs in Recursing On Children and Keys.
You can inspect this effect if you use the browser extension "React Dev Tools". With the extension active, you can do the following:
open browser developer tools
got to "Component"-tab
go to tab settings (gear icon)
toggle "Highlight updates when components render."
try to insert an item of the beginning of a list with no key, and then again with a list which uses unique keys, an check which Components update
Using an index based key will work just fine, however performance wise it is not very efficient in cases where the dynamic list is updated at lower indices.

Key is like the id of the item, it's a bad practice to use indexes because if one of the items change his position in the list all the list will rerender, you probably can find cases that it will not affect your performance so much but I think it's a bad habit.
check this out: Why Are Keys Important In React

Related

Atom Array Element Not Deleting Properly

Within my page (index.tsx), I render a component which can have many children (grandparent.tsx). When I hit delete with no option selected for the input, it deletes fine from my global state (testAtom in atom.ts). However, if I select an input option, when hitting delete, it resets the input option i.e. it doesn't delete in the intended way. The intended behaviour is for it to remove the relevant index from the testAtom.
I believe it is something to do with the useEffect within the input.tsx component. I currently have this here because if the user changes the condition dropdown, I want the value to update accordingly. However, I've been stuck on this for a while and cannot figure it out, so any pointers would be helpful. I would recommend a look at the codesandbox link below, as it will give a clearer picture to the overall makeup of this problem.
CodeSandboxLink

Performance issue with big React state object

I have a function updateRowValue, which updates a value in a row in a table based on user input. There are 11 columns whose values can be updated by the user, and the user can add/remove rows. I am currently testing on a table with 7 rows, and there is a severe lag between pressing a key on the keyboard and the change appearing on the screen.
Each time the user presses a key on their keyboard, the state is updated (I am using a controlled MUI TextField). I don't think the amount of inputs is the actual problem (though I might be wrong). I think the problem is that I have to traverse through the table_rows object every time a value should be updated.
My question is basically: Can I update the value each time the user presses a key on their keyboard and also make the app feel responsive to the key presses? If so, then how?
function updateRowValue(row_id, attribute, new_value){
const new_row = Object.assign(table_rows[row_id], {[attribute]: new_value});
const newState = Object.assign(table_rows, { [row_id]: {...table_rows[row_id], ...new_row} });
setTableRows({...table_rows, ...newState});
}
I found an alternative to the whole mess I had created. Instead of trying to optimize how I save the states (which might not even be possible) I realized that I don't actually need the states. I can access the form values directly.
By adding inputProps={{"data-row": row_id, "data-cell": cell_id}} to the TextField, and then using document.querySelectorAll("[data-row]"), it is easy and fast to access all cells and their data when it is actually relevant to use them (saving them to the database in my case).

Why should a developer create keys for child elements in React?

Why should a developer create keys for child elements in React? Why can't React itself do this?
React uses the unique keys you provide for the items in a list to compare lists when generating a mutation. That is, the state has changed - items have been added to or removed from the list - and React needs to update the DOM.
A list may contain items with the same content, so that React cannot generate unique keys from the items. Also, how would it know which list items refer to the same object in the state? For a static list that never changes, that is not a problem (and you can safely use the list index as a key), but for lists that change, it is.
It is therefore important that you provide React with a unique key, and you have the opportunity to provide it with a key that allows it to make efficient comparisons - say, compare items by numeric ID, and not comparing strings.
See also https://reactjs.org/docs/reconciliation.html#recursing-on-children for more information.
Keys help React identify which items have changed (added/removed/re-ordered). To give a unique identity to every element inside the array, a key is required. Each item in the array has an id associated with it. Hence, this is the id that is assigned as a key for each item.
You can read more on this medium blog post.
First of all, you can control in what condition should a component re-render by giving it a key prop. You can check the codesandbox, everytime you click the button, the key to the jsx element changes, causing it to re-render and get the animation effect.
Also, react shouldn't decide what keys should your jsx element get. If it is defaulted to an index like:
arr.map((item, index) => <div key={index}>...
Then, you may run into issues when you need to sort, update, change the array. Here is a good example
Keys help in React js to identify which objects have changed, or added, or are removed. Each child in an array should have a unique "key" prop. Check the render method of the Table Component. It helps to keep track whenever you change the component.

What is causing the input box text not to re-order in the ReactJS page example of Reconciliation?

In the ReactJS page of Reconciliation, there are two examples:
an example of the issues that can be caused by using indexes as keys
an updated version of the same example showing how not using indexes as keys will fix these reordering, sorting, and prepending issues
As of Feb 18, 2020, there is no telling of how to reproduce the issue on the page. I tried clicking on "Add New to Start" or "to End" a few times and reorder the list, and they seemed to work fine. Only later I found out you need to enter some text into the box, and then just "Add New to End", and do it three times, and reorder the list.
In the first example, the text in the input box were not re-ordered. In the second example, the text in the input box were re-ordered as expected.
The two programs differ by using
<ToDo key={index} {...todo} />
vs
<ToDo key={todo.id} {...todo} />
There are also some slightly re-ordering of code and using todoCounter vs toDoCounter (capital D) between the two versions, which I wonder why and the React team might fix it later. But you can modify the first version from key={index} to key={todo.id} and you can also see the problem solved.
But then when I looked into the code, the input box doesn't actually add the text data to the state property list (an array). Only id and createdAt are added per new entry to list.
So while we can say using key={todo.id} fixed the problem, what was causing the problem in the first place?
You can say that the id and createdAt are sorted correctly. What is not sorted correctly are the input boxes... but according to the rule of reconciliation to decide whether to refresh actual DOM elements on the page, the new Virtual DOM subtree is compared to the previous virtual DOM subtree.
Now the "property" of the input box value is not part of the virtual DOM, unless if React actually silently puts the value into the virtual DOM.
So when "diff'ing" recursively, React should think the input boxes are all the same. So is this how it worked: if we used the key={index}, now React diff'ed each column in the current virtual DOM subtree with the previous one, and see that the "ID" and "createAt" cells are different, therefore forcing a refresh to the actual DOM. React saw the input boxes all the same and didn't bother to force a refresh to the actual DOM.
However, if we use key={todo.id}, now React will think, the whole row is different, because the row "id" has changed. So React will force a refresh to the actual DOM for the whole row, including the input box.
So we can say, this bug occur only when some data is not in the virtual DOM subtree... which is rare, such as in this current case. In other cases, all the data would be given out by the render() of class component or the return of function component, and therefore, able to tell ReactJS that, "yes, force a refresh to the actual DOM".
Is this really how it worked?
Using index as keys is very dangerous and is highly advised against.
For example, if you delete an item from the middle of the list, another item will take its place, React needs a unique key to recognize a DOM element as an individual/independent element and not something that is temporary or useless.
Your list is being sorted as intended, you can see that the ID column is sorted accurately.
But in your case, React doesn't know which todo belongs to which ID, indexes are not todo list ID, they're numbers that were generated in order.
Imagine this
Todos:
id 1
todo: "take out the trash"
id 2
todo: "make dinner"
if you sort your todos by the shortest string
it will end up like this:
2
1
the normal order is:
1
2
No matter how you sort your list and change its order, if you tell React to use indexes, it will always render it like this:
1
2
3
4
...etc
If you use object IDs, React will keep track of the elements and will connect the keys to the IDs, then render your elements based on the order of your sorted IDs.

Material UI Popover is thrown to the top left when used on an inline button in a table with a unique key

I'm having some issues when using shortid or any other unique uid generator. The moment I use shortid.generate() as key in a table, the anchor point of my Material UI Popover is thrown to its default position rather than appearing where the button is.
Here's a sandbox! - try removing/adding back shortid.generate() from line 72.
I even tried uniqueId from lodash and the same thing happens - not using a key does render the dialog on the right place though. I even changed versions of Material UI/React and nothing happened.
Any ideas?
Thanks!
EDIT - I usually use item.uid as key since I always fetch items from a service, but if I just created the object, item.uid is undefined - what I did until now was to set item.uid = shortid.generate() (a temporary uid) when I create the object and then just leave <TableRow key={item.uid}> as is. But then I have to remove the temporary uid before I save the object.
You should never use random ids as keys (at least not random ids generated during render).
When your state changes (e.g. due to handleClick) your table rows will all be re-rendered with new keys. This means that instead of a simple re-render, all of the table rows will be unmounted and new DOM elements mounted. The anchorEl in your state will point at an element that has been removed from the DOM, so its position cannot be determined.
If you don’t have unique keys related to your data, then just use an index as a key so that it is at least stable across renders (so long as you aren’t adding/removing rows). Another option would be to generate the unique ids when creating your data instead of during rendering.

Resources