I'm pulling data from a GraphQL backend. Here is the data that I'm pulling.
{
"data": {
"product": {
"attributes": [
{
"id": "Capacity",
"items": [
{
"id": "256GB",
"displayValue": "256GB"
},
{
"id": "512GB",
"displayValue": "512GB"
}
]
},
{
"id": "With USB 3 ports",
"items": [
{
"id": "Yes",
"displayValue": "Yes"
},
{
"id": "No",
"displayValue": "No"
}
]
},
{
"id": "Touch ID in keyboard",
"items": [
{
"id": "Yes",
"displayValue": "Yes"
},
{
"id": "No",
"displayValue": "No"
}
]
}
]
}
}
}
I'm then iterating through the attributes
array and giving unique IDs, _id
to differentiate the different items
. It turns out that the item
that has an ID With USB 3 ports
and Touch ID in keyboard
always get the same ID. I'm using nanoid.
Here is the code
import { PureComponent } from "react";
import { useParams } from 'react-router-dom';
import { Query } from '@apollo/client/react/components';
import GET_PRODUCT from "../../graphql/getProduct";
import { nanoid } from 'nanoid';
import ProductDetailComponent from "./ProductDetailComponent";
import './ProductDetail.css'
class ProductDetail extends PureComponent {
render() {
const { id } = this.props.params;
const { currency, addToCart, closeCurrencyOverlay } = this.props;
return (
<Query
key="yes"
query={GET_PRODUCT}
variables={{ id }}
fetchPolicy="network-only"
>
{({ loading, data}) => {
if (loading) return null;
let newAttributes = [];
data.product.attributes.map((attribute) => {
attribute.items.map((item) => {
item._id = nanoid(); // <-- Giving the id from here
})
newAttributes.push(attribute)
})
const newData = { ...data, attributes: newAttributes }
return <ProductDetailComponent data={newData} currency={currency} addToCart={addToCart} closeCurrencyOverlay={closeCurrencyOverlay} id={id}/>
}}
</Query>
)
}
}
export default (props) => (
<ProductDetail
{...props}
params={useParams()}
/>
);
I'm just wondering why they always get the same ID.
Here is a screenshot of the Chrome dev tools
CodePudding user response:
If you are always getting the same id for the elements inside the items
array and looking at your code where you assign item._id = nanoid();
it makes me think that those objects share the same reference.
Here is a quick example to illustrate why this would happen:
// reference of array values
const yesNoArr = [
{ id: 1, text: 'Yes' },
{ id: 2, text: 'No' }
];
const objs = [
// here both objects share the same reference of "items" property:
{ items: yesNoArr, text: 'with USB 3 ports' },
{ items: yesNoArr, text: 'Touch Id in keyboard' }
];
// if at this point you try to set _id to items[0]:
objs[0].items[0]._id = 123; // this will work fine and assign _id = 123
objs[1].items[0]._id = 456; // but this will overwrite previous "123" by "456" because they point to the same object
// result:
console.log(JSON.stringify(objs, null, 2));
If you want to avoid this behavior you'll have to make a modified copy of the items
so that they don't point to the same referenced object.
Try replacing this piece of code and see how it behaves:
const newAttributes = data.product.attributes.map((attribute) => {
return {
...attribute,
items: attribute.items.map((item) => {
// make a copy of each item to get a different reference:
return {
...item,
_id: nanoid(),
};
}),
};
});
console.log(newAttributes);