So I have the code below, with bookList_object being a bunch of JSON objects I fetched from backend.
_renderBookList = () => {
const books = this.state.bookList_object.map((this_book, index) => {
return <BookList
body={this_book.body}
created_at={this_book.created_at}
key = {index}
/>
})
Now, I've learned that there has to be key when having multiple children. So I've used index parameter to assign each id to the key prop. But then I get this error:
"BookList: key is not a prop. Trying to access it will result in undefined being returned. If you need to access the same value within the child component, you should pass it as a different prop."
What have I done wrong?
Also, I already have id field in each of my object. Is there a way to use id instead of key?
Thank you very much in advance. :)
I recently had this Warning. It happened because I tried to assign a key to some elements in two different parts of the code. The warning pops up when the key prop is accessed a second time.
Find out where else the component is being assigned a key. Then choose only one place to assign a key.
Related
How can I access the key property of a component. I thought it would be in this.props but it's not.
e.g.
<ProductList
key = {list.id}
listId = {list.id}
name = {list.name}
items = {list.items}
/>
and in product list if I do
console.log(this.props)
returns
Object {listId: "list1", name: "Default", items: Array[2]}
With no key property at all. I can create another property and assign the same value to it, but it seems redundant since the key property is already being used.
Also, does the key property have to be unique in the entire component, or just in the loop or collection it's being rendered from?
The key property is used by React under the hood, and is not exposed to you. You'll want to use a custom property and pass that data in. I recommend a semantically meaningful property name; key is only to help identify a DOM node during reconciliation, so having another property called listId makes sense.
The key property does not need to be unique for the whole component, but I believe it should be unique for the nesting level you're in (so generally the loop or collection). If React detects a problem with duplicate keys (in the development build), it will throw an error:
Warning: flattenChildren(...): Encountered two children with the same key, .$a. Child keys must be unique; when two children share a key, only the first child will be used.
Key : this._reactInternalFiber.key
Index : this._reactInternalFiber.index
Generating a <select><option> dropdown (DD) from an array of objects. Added an id property to the objects to supposedly fix the dreaded "Each child in a list should have a unique 'key' prop" warning.
Sample data:
{id:70, city:"Allentown", state:"Pennsylvania"},
{id:71, city:"Alliance", state:"Ohio"},
{id:72, city:"Alliance", state:"Nebraska"},
Sample code:
<select name="cities" id="cities" onClick={selectCity}>
{cities.map((x) => (<option key={x.id} value={x.city}>{x.city}</option>))}
</select>
Note: The DD, and the array that populates it, is initially empty when the component loads. It's populated by a click event in another DD.
Spent a goodly chunk o' time trying out several SO suggestions. Tried wrapping the <select> in a <React.Fragment> block, and added a key to that. Tried using both 70 and '70' for the id property. Warning still appears. Using the array index as the key eliminates the warning, but I can't use that method because of other problems it causes.
Questions:
-- Data is coming from a JS file that I'm importing, is that a factor?
-- Is the array initially being empty on component load causing this?
-- What's the proper way to add the key prop to avoid the warning?
Thanks ahead of time for any help you awesome SO people can provide!
Answering my own question. In the initial post I asked "Is the array initially being empty on component load causing this?" The answer is yes, the empty array produces the warning.
When the component first loads, in map() it looks for a valid value for [array element].id. At this point, the array is empty, and has no ids loaded. It normally remains empty until an onClick event loads it with values. Had to add an initial default value to avoid the 'unique keys' warning. The fix is:
BAD (produces unique key warning):
const [cities, setCities] = useState([]);
GOOD (no warning): const [cities, setCities] = useState([{id: 0, city: '', state: ""}]);
How can I access the key property of a component. I thought it would be in this.props but it's not.
e.g.
<ProductList
key = {list.id}
listId = {list.id}
name = {list.name}
items = {list.items}
/>
and in product list if I do
console.log(this.props)
returns
Object {listId: "list1", name: "Default", items: Array[2]}
With no key property at all. I can create another property and assign the same value to it, but it seems redundant since the key property is already being used.
Also, does the key property have to be unique in the entire component, or just in the loop or collection it's being rendered from?
The key property is used by React under the hood, and is not exposed to you. You'll want to use a custom property and pass that data in. I recommend a semantically meaningful property name; key is only to help identify a DOM node during reconciliation, so having another property called listId makes sense.
The key property does not need to be unique for the whole component, but I believe it should be unique for the nesting level you're in (so generally the loop or collection). If React detects a problem with duplicate keys (in the development build), it will throw an error:
Warning: flattenChildren(...): Encountered two children with the same key, .$a. Child keys must be unique; when two children share a key, only the first child will be used.
Key : this._reactInternalFiber.key
Index : this._reactInternalFiber.index
I have a list of objects which I want to render as a list using React.
React requires the special key attribute on the list element in order to track changes between DOM and virtual DOM.
My object doesn't have any special unique properties like id, in fact, two objects could even have the same properties, but the object itself is unique.
Is there a way to use object's reference as a key to render React list?
Otherwise, what workarounds could be suggested in this case?
Also, I'm receiving the list of items from a third party, so I can't just attach a random ID to each item, because it will make the React to re-render all items each time I receive updated data.
If you don't have a unique ID (returned by the back-end, or you can't build a composite key on the front-end), then you can use the array index as key:
render() {
return array.map((item, index) => <Item key={index} />)
}
Without having an unique ID, whatever approach you choose, if the order of the items is changed, then the list can get totally messed up.
It would be safe enough to use this approach if your case met the following 3 conditions, thanks to this article:
the list and items are static–they are not computed and do not change.
the items in the list have no ids.
the list is never reordered or filtered.
Using array's index as a key would be a disaster, because if array changes, the view will not be properly updated or will be updated partially in an unpredictable manner.
The only solution I've found is to generate the dummy IDs as suggested in the comments.
The idea is to implement the generator, which will create an ID for each item, but at the same time will remember previously generated IDs.
The WeakMap JavaScript class will allow us to store the mapping information between the item object reference and generated ID:
export class IdGenerator {
// Using WeakMap as index
map = new WeakMap();
// Internal counter
lastId = 0;
getItemId(item) {
// Getting previously generated ID for the specified item
let id = this.map.get(item);
// If item is new, generating the ID and storing it for future use
if (undefined === id) {
id = ++(this.lastId);
this.map.set(item, id);
}
// Returning ID to the caller in either case
return id;
}
}
Then it could be used to assign generated IDs to each item before rendering the list:
class ListBox extends Component {
// Instantiating the generator
itemsIdGenerator = new IdGenerator();
render() {
return (
<ul>
{this.props.items.map(item => (
<li key={this.itemsIdGenerator.getItemId(item)}>
{item.title}
</li>
))}
</ul>
);
}
}
Make sure to use different instance of the generator for each instance of the component though and create generator only once with the component.
Also, calling the generator for each item in component's render method could be sub-optimal. However, I didn't want to touch the original items or mutate them. If this is an issue, consider using various lifecycle methods provided by React to set IDs when props changes.
How can I access the key property of a component. I thought it would be in this.props but it's not.
e.g.
<ProductList
key = {list.id}
listId = {list.id}
name = {list.name}
items = {list.items}
/>
and in product list if I do
console.log(this.props)
returns
Object {listId: "list1", name: "Default", items: Array[2]}
With no key property at all. I can create another property and assign the same value to it, but it seems redundant since the key property is already being used.
Also, does the key property have to be unique in the entire component, or just in the loop or collection it's being rendered from?
The key property is used by React under the hood, and is not exposed to you. You'll want to use a custom property and pass that data in. I recommend a semantically meaningful property name; key is only to help identify a DOM node during reconciliation, so having another property called listId makes sense.
The key property does not need to be unique for the whole component, but I believe it should be unique for the nesting level you're in (so generally the loop or collection). If React detects a problem with duplicate keys (in the development build), it will throw an error:
Warning: flattenChildren(...): Encountered two children with the same key, .$a. Child keys must be unique; when two children share a key, only the first child will be used.
Key : this._reactInternalFiber.key
Index : this._reactInternalFiber.index