I've been recently studying the importance of elements' keys in React and I'm still a little confused about it.
I have this case where there is datagrid component responsible for presenting data in rows. There is implemented sorting, filtering and pagination. I fetch the rows data from api.
And the question is:
How should I properly choose keys for map function elements (rows)?
The thing is that the already fetched row data can change between refreshes (by being edited by other user).
Should I use unique ids for key properties (for instance nanoid) or use row.id?
If I understand it correctly, using row.id would result in no row rerendering for already loaded rows even if some of row data were changed.
On the other hand, using unique ids (like nanoid) would make negative impact on performance while sorting, filtering etc...?
Do I get this right, and how to do this properly then?
I agree to study the key in more details. Here's my understanding.
If I understand it correctly, using row.id would result in no row rerendering for already loaded rows even if some of row data were changed.
This is not true, the purpose of the key is to identify the item location, so when it comes to the reconciliation (convert element into a node), React knows if a node (fiber) can be reused to speed up the update process.
Let's make an example, if initially the the key order is
1 2 3
And somehow after a re-order, it becomes the following in the new update
2 1 3
Then React is guided to use the node with key 1 to update the item. The item is updated, and in this case, to the right place. BTW, React doesn't know by default the order of items, to React, they look all similar ;)
I'm only giving you a basic case. But you can make a guess for other cases, ex. with one key missing, or a new key added.
Keys should always be unique, stable (as mentioned above by Brian), and never manipulated in your code (hence stable).
A unique ID is preferred. I've seen people pass an entire user object as the key. Let along that there might be duplicate users, this could also be an enormous key.
Do not use the index of any array as your key.
Related
I'm currently working on a React project where I display cards with informations from an array of Objects, so that their content is dynamic.
Now I would like to implement a like and a dislike counter on each card to vote the articles.
My problem is, how to fix this, so that when I click on (like), that it just ads 1 to that specific card and not in all of them.
Because I'm mapping through the data array.
Look into using the .map method in your JSX. It allows you to iterate through multiple values and you can set your methods to individually mapped elements. Make sure to include a key in the element as well.
Further info:
https://reactjs.org/docs/lists-and-keys.html
In my app I import a JSON file and then display it in a table. The table has 3 columns.
Two issues with this:
The table also has sorting and CRUD operations, so using the array index for the row key is not best practice.
Similar with above, I am trying to implement the functionality to edit a row, and doing so by index is finicky because they may have shifted from sorting or CRUD.
I came up with some potential solutions, but they all seem weak:
Uniquely identify a row by a combination of its visible columns. This has problems because while it's extremely unlikely there will be duplicate rows for this data set, I suppose it's possible.
Uniquely identify a row by fields that are relatively guaranteed to be unique. For example, each row has a creation date, and since all rows are generated manually in the program, I believe this could function as an ID for uniquely identifying rows? It kinda feels like identifying a row by a date is bad for some reason though.. Not sure.
Uniquely identify a row by a combination of all its fields (visible and hidden). May be slow or needlessly complex?
Upon row creation, assign an ID to the row that isn't shown to the user and is persisted in the saved file. Would also probably need to have a global counter variable saved (because if a row is deleted we wouldn't want to re-use that ID and we need to know where we left off from).
Assign a GUID to each row? Wouldn't need to store an index counter but more space would be required (although the files are relatively small so I'm not sure how much this matters).
Are there any other or better ways that I'm missing? How is this normally handled?
I'd go with 4 or 5, but with a minor change:
You can keep the JSON as is. Upon importing the JSON, you map each item and assign them an ID or GUID - I'd use ID for simplicity, but it's up to you. Within the execution of your program, this field will be used to the CRUD operations and/or sorting.
When your program is done, map again your data, and save back in the same format you've received (removing the new prop you've added).
I have a dgrid with inputs. My dgrid is in edit mode so users can add rows, edit rows and sort rows. When I look at my collection, it looks like
id: "1"
COLUMN1: "INPUT TYPE="text"...."
COLUMN2: "INPUT TYPE="text"...."
Note: I could not put the greater than and less than above with describing the inputs.
and so on. When I make a change on screen, for one of the inputs, the collection is not updated. So, when I enter a new row or sort the dgrid, all the entered data refreshes back to its original state. I know the reason is because the collection is not updated. Is it possible to update the collection or do I have to write my own code to do so? Please note, I am using a dgrid and NOT an onDemand grid.
Thanks for your help in advance.
Please add some code on how you are initializing your collection and what all mixins are being used for it. As far as I can understand your problem, you need to add the Trackable mixin in your collection. Read its documentation here.
From the documentation:
We can add support for tracking the position of updates by using the Trackable mixin. With the Trackable mixin added to a store, we can call the store's track method to get a collection that includes index information in its delete, add, and update events.
This might be of your help.
How to hide specific rows in Handsontable.
I have buttons in DOM, each of them should hide specific rows.
For example: click button with class `alarm' should shide all rows which second column has value 'alarm'.
For now i do ugly thing. Every button click i loop overy my tableData and delete datas with 'alarm' then just load data and render table. But i can't do that becouse i have some dynamic datas so after render their disapear.
eveGrid.loadData(tableData);
eveGrid.render();
Dynamically hiding rows is quite a complex task which is not documented in the Handosntable documentation. However, there is plenty of functionality available to implement it ourselves. As a matter of fact, I had to do this just last week so I can share with you a potential approach. It's really quite simple and similar to your solution, which is not ugly by the way!
What you want to do is keep two copies of your data. The first we can call data, and the second activeData. Initially, they both equal each other. Now this part is tricky and not easy to grasp but what you have to make sure is that these two arrays are DIFFERENT OBJECTS, but have the SAME REFERENCED ELEMENTS. What this means is that the arrays themselves are not clones, so an equality test would fail. However, their elements are clones. Your activeData elements are just references to the elements on data.
Once we have this set up, it's simple to implement the hiding of rows. Your click should look through data and set a new activeData based on the matching rows you want to display. Then just update handsontable with something like:
hotInstance.updateSettings({data: activeData});
And that's it! Note that that updating method will automatically trigger a render().
Let me know how it goes, I'm curious to see if other people can use this approach.
Surprised this question hasn't been asked before, at least I haven't been able to find it.
I have multiple lists or elements that are all connected together using jQuery UI sortable. Its all working as the docs say it should, however I would like to make the lists force sort. For example, when each list is loaded in, the items are ordered by date. When you move an item from list 1 into list two, you can place that item anywhere in the list, regardless of its date. Similarly you can move items within the same list so they are out of date order.
Is there a way to make it calculate the date ordering and render the items in the correct order once an item has been moved?
Once an item is moved from one list to another it performs an ajax request to save that item to that list, a possible solution would be for that request to return that list in a sorted order and then re-render that list, however that doesnt seem like a particularly clean approach as you already have all the data.
Any easier/better ways of doing this? If its also possible to prevent reordering within the same list that would be ideal.
thanks.
JQuery Sortable does not handle the behavior you are trying to achieve. You have to do it your self. I think the receive eventHandler is a good starting point. you can use ui.item.prev() and ui.item.next() to retrieve the previous and the next elements then check if the insertion point matches your requirements and insert the element to that position or turn the element back to it's old list/position ( $(ui.sender).sortable("cancel"); )