So I have to manipulate a large state or array of addons to show it as a summary. So I want to have the format below
[
'addonGroupIndexHere' =>[
"13249" => {
label: "whatever the label is",
price: 10.00,
sku: "13249",
qty: 1,
total: 10.00
},
"13250" => => {
label: "whatever the label is",
price: 5.00,
sku: "13250",
qty: 3,
total: 15.00
},
],
'addonGroupAnotherIndexHere' =>[
"13251" => {
label: "whatever the label is",
price: 10.00,
sku: "13251",
qty: 1,
total: 10.00
},
"13252" => => {
label: "whatever the label is",
price: 5.00,
sku: "13252",
qty: 3,
total: 15.00
},
],
]
so I declared a state in a context like below
const [addonSummary, setAddonSummary] = useState([])
An addon is an object like
{
label: "whatever the label is"
price: 10.00
sku: "13249"
}
So when a user adds 1 quantity of an addon I will have to modify the addon object like below
{
label: "whatever the label is"
price: 10.00
sku: "13249",
qty: 1,
total: 10.00
}
So if its 2 quantities then I need the object to be like below
{
label: "whatever the label is"
price: 10.00
sku: "13249",
qty: 3,
total: 30.00
}
I don't have a problem setting or updating the addon object but my problem is when I set the addon summary state with a specific addon group index.
const addonItem = {
label: "whatever the label is"
price: 10.00
sku: "13249",
qty: 3,
total: 30.00
}
const addonGroupIndex = 0 // or 1 or 3. This is actually a state selected by the user
I am having problem manipulating an array/object to have the following output
[
'addonGroupIndexHere' =>[
"13249" => {
label: "whatever the label is",
price: 10.00,
sku: "13249",
qty: 1,
total: 10.00
},
"13250" => => {
label: "whatever the label is",
price: 5.00,
sku: "13250",
qty: 3,
total: 15.00
},
],
'addonGroupAnotherIndexHere' =>[
"13251" => {
label: "whatever the label is",
price: 10.00,
sku: "13251",
qty: 1,
total: 10.00
},
"13252" => => {
label: "whatever the label is",
price: 5.00,
sku: "13252",
qty: 3,
total: 15.00
},
],
]
I tried doing but it doesn't work.
addonSummary[addonGroupIndex ][addonItem.sku] = addonItem
The follow are the the codes I have. ProductContext.js
import { isEmpty } from 'lodash';
import { useState, createContext, useRef, useEffect } from 'react';
import { v4 } from 'uuid';
export const ProductContext = createContext()
export const ProductContextProvider = (props) => {
const [selectedAddonGroup, setSelectedAddonGroup] = useState(-1);
const [addonSummary, setAddonSummary] = useState([])
const updateAddonSummary = (addonItem) => {
// console.log('updateAddonSummary', addonItem)
// console.log('selectedAddonGroup', selectedAddonGroup)
if (!isEmpty(addonSummary[selectedAddonGroup])) {
addonSummary[selectedAddonGroup] = [addonSummary[selectedAddonGroup], addonItem]
} else {
addonSummary[selectedAddonGroup] = addonItem
}
}
return (
<ProductContext.Provider value={{
addonDialog: {
selectedAddonGroup, setSelectedAddonGroup,
summary: addonSummary, updateSummary: updateAddonSummary,
setAddonSummary
}
}}>
{props.children}
</ProductContext.Provider>
)
}
AddonsQuantityFields.js
import { GbMinus, GbPlus } from '@gb-components/icons'
import { useEffect, useState } from 'react'
import classNames from 'classnames'
import { useProductContext } from '@gb-utils/product/product';
export default function AddonsQuantityFields({ addonItem }) {
const { addonDialog } = useProductContext();
const [quantity, setQuantity] = useState(0)
function updateQuantity(action, qty) {
if ('minus' == action) {
addonDialog.deductAddonQty(qty)
qty = quantity - qty
} else {
addonDialog.increaseAddonQty(qty)
qty = quantity qty
}
if (qty <= 0) {
// if qty is zero then we need to remove it from the summary
} else {
const total = qty * addonItem.price;
const item = {}
item[addonItem.sku] = addonItem
addonDialog.updateSummary(item)
console.log('addonDialog.summary', addonDialog.summary)
}
setQuantity(qty)
}
return (
<div className='flex w-[96px] justify-end gap-0'>
<button className={classNames(
quantity <= 0 ? 'hidden' : '',
'flex items-center justify-center m-0 h-[32px] w-[32px] border border-[#E4E4E4] bg-[#F7F7F7]'
)} onClick={() => updateQuantity('minus', 1)}>
<GbMinus />
</button>
<input
readOnly
className={classNames(
quantity <= 0 ? 'hidden' : '',
'h-[32px] w-[32px] border border-[#E4E4E4] focus:ring-0 text-sm text-center text-[#656565] p-0'
)}
type={'number'}
inputMode='numeric'
autoComplete='off'
value={quantity}
step="1" min="0"
/>
<button
className='flex items-center justify-center m-0 h-[32px] w-[32px] border border-[#E4E4E4] bg-[#F7F7F7]'
onClick={() => updateQuantity('add', 1)}>
<GbPlus />
</button>
</div>
)
}
then the component that shows the Addon component is below
AddonItem.js
import { GbAddonItemImgPlaceholder, GbChevronDown, GbChevronUp } from '@gb-components/icons'
import classNames from 'classnames'
import { isEmpty } from 'lodash'
import React, { useState } from 'react'
import AddonsQuantityFields from '../quantity-fields/AddonsQuantityFields'
export default function AddonItem() {
const addonItem = {
label: "whatever the label is"
price: 10.00
sku: "13249"
}
return (
<ProductContextProvider>
<AddonsQuantityFields addonItem={addonItem} />
</ProductContextProvider>
)
}
CodePudding user response:
You cannot use arrays with keys in JS
.
You should use an object
if you want to use a key for each addonItem.
var addonSummary = {};
const addonItem = {
label: "whatever the label is",
price: 10.00,
sku: "13249",
qty: 3,
total: 30.00
}
const addonGroupIndex = 0;
if(!addonSummary.hasOwnProperty(addonGroupIndex))
{
addonSummary[addonGroupIndex] = {};
}
if(!addonSummary[addonGroupIndex].hasOwnProperty(addonItem.sku))
{
addonSummary[addonGroupIndex][addonItem.sku] = {};
}
addonSummary[addonGroupIndex][addonItem.sku] = addonItem;
console.log(addonSummary);
This gives the following structure:
{
"addonGroupIndexHere": {
"13249": {
"label": "whatever the label is",
"price": 10,
"sku": "13249",
"qty": 3,
"total": 30
},
"13250": {
"label": "whatever the label is",
"price": 5,
"sku": "13250",
"qty": 3,
"total": 15
}
},
"AnotheraddonGroupIndexHere": {
"13251": {
"label": "whatever the label is",
"price": 10,
"sku": "13251",
"qty": 3,
"total": 30
},
"13252": {
"label": "whatever the label is",
"price": 5,
"sku": "13252",
"qty": 3,
"total": 15
}
}
}
Hope this helps :)
CodePudding user response:
In React state must be immutable. When you update it, in this case the array addonSummary, you have to use the hook setAddonSummary, not change it directly like this addonSummary[addonGroupIndex ][addonItem.sku] = addonItem
.
Next, state management is not a certain laws, everyone could do the same thing in very different way, so you have to chose your strategy.
the easiest thing would be something like this:
export function addOnList(data = {}) {
const [addonSummary, setAddonSummary] = useState(data);
function updateElement(summaryIndex, key, newValue = {}) {
const oldSummary = addonSummary[summaryIndex];
const newSummary = Object.keys(oldSummary).reduce((previous, current) => {
previous[current] = current !== key ? oldSummary[current] : newValue;
return previous;
}, {});
const newData = {
...addonSummary,
...{ [summaryIndex]: newSummary }
};
setAddonSummary(newData);
}
return (
// on the element which the user click, change etc... to update the state
// you set the attribute
// onClikc={ (event) => updateElement(summIndex, elementKey, newAddonElement)}
);
}
The render method is super pseudo code, basically from the event you have to build the new addOn object, and pass it to the update function with the corresponding group index and sku (key). And in this way you can correctly update the state addonSummary wich contains the main array you posted