Is there a way to create multiple counters using a state - javascript

I tried to create a counter which is included in a mapped object so each time i click on the increment buttons it increase all the four counters at once meanwhile i want to use only three states: the counter, increase button and decrease button
const [details, setDetails] = useState([
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
counter: 0,
id: 1,
increseBtn: 1,
},
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
decrecreseBtn: '-',
increseBtn: 2,
price: 0,
remove: 'Remove',
id: 2
},
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
decrecreseBtn: '-',
increseBtn:3,
remove: 'Remove',
id: 3,
counter: 0,
},
{
picture: Lantern,
bookTitle: 'Quantitative Reasoning',
school: 'Primary School',
decrecreseBtn: '-',
increseBtn: 4,
remove: 'Remove',
counter: 0,
id: 4,
// increseBtn: {increase},
},
]
)
const increment = (id) => {
setcounter(counter+1)
}
const decrement = () => {
setcounter(counter-1)
}
<button onClick={decrement}>-</button>
<small>{counter} </small>
<button onClick={()=> increment(add.id)}>+</button>

You can update by passing an id to the increment and decrement function
const increment = (id) => {
setDetails(details.map(item => {
if (item.id === id) {
// Create a *new* object with changes
return { ...item, counter: (item.counter ?? 0) + 1 };
} else {
// No changes
return item;
}
});
}
const decrement = (id) => {
setDetails(details.map(item => {
if (item.id === id) {
// Create a *new* object with changes
return { ...item, counter: (item.counter ?? 0) - 1 };
} else {
// No changes
return item;
}
});
}
// in the markup
<button onClick={() => decrement(add.id)}>-</button>
<small>{add.counter} </small>
<button onClick={()=> increment(add.id)}>+</button>
You can read more about working with arrays in React
Hope it helps

Just set the counter for the objects where id matches after each increment and decrement operation.
details = details.map(obj => {
if(obj.id === id) {
obj = {
...obj,
counter
}
}
retrun obj;
})

Related

How to change multiple Boolean values()Parent Item, Child Item) in an array on click

I am working in a task where I need to change the Boolean values onclick from an array of items, I need to change the Boolean value from array and child array. I have changed the value from an array it's working as expected but inside that array I am having an another array of objects from that I need to change the boolean value. OnClick I need to make parent isProcessing and child isProcessing false. I have tried changing it but I am not getting the expected output , can anyone guide me how to achieve this thanks in advance.
Mock Data:
const mockItems = [
{
id: '1',
itemType: 'metal',
doneBy: {
id: '1',
display: 'Item Name',
},
catg: 'A',
createdDate: '01/01/2021',
updatedBy: {
id: '1',
type: 'M-A',
},
isProcessing: 'true',
subItems: [
{
id: '1',
doneBy: {
id: '1',
display: 'sub item name',
},
status: {
type: 'notapproved',
},
isProcessing: 'true',
},
],
},
];
Code for accessing parent : isProcessing //it's working
const [processingItem, setProcessingItem] = useState(mockItems);
const handleToggle = () => {
setProcessingItem((prevState) =>
prevState.map((prItem, i) =>
i === 0 ? { ...prItem, isProcessing: false } : prItem
)
);
};
//code to change child array Boolean not working
const handleToggle = () => {
setProcessingItem((prevState) => {
prevState.map((prItem, index) => {
if (index === 0) {
const obj = { ...prItem.subItems, isProcessing: false };
return { ...prItem, isProcessing: false, obj };
}
});
});
};
Try this
const handleToggle = () => {
setProcessingItem((prevState) => {
prevState.map((prItem, index) => {
if(index !=0)
return prItem;
const subItems = prItem.subItems.map((si,idx)=>{
return idx != 0 ? si : {...si,isProcessing: false}
});
return { ...prItem, isProcessing: false, subItems:subItems }
}
)
}
)
}

How to update deeply nested array of objects?

I have the following nested array of objects:
const data = [
{
product: {
id: "U2NlbmFyaW9Qcm9swkdWN0OjEsz",
currentValue: 34300,
},
task: {
id: "R2VuZXJpY1Byb2R1Y3Q6MTA",
name: "My Annuity",
},
instrumentDetails: [
{
instrument: {
id: "U2NlbmFyaW9JbnN0cnVtZW50OjEz",
supplier: {
id: "U3VwcGxpZXJJbnN0cnVtZW50OjUzNjQ",
supplierDetails: {
name: "Local - Class A",
},
},
currentValue: 44323,
},
assets: {
current: 1.2999270432626702,
fixed: 0.5144729302004819,
financial: 0.0723506386331588,
cash: 0.00006003594786398524,
alternative: 0.05214078143244779,
property: 0.548494862567579,
local: 0.10089348539739094,
global: 0,
},
},
],
},
{
product: {
id: "U2NlbmFyaW9Qcm9swkfefewdWN0OjEsz",
currentValue: 3435300,
},
task: {
id: "R2VuZXJpYfewfew1Byb2R1Y3Q6MTA",
name: "Living",
},
instrumentDetails: [
{
instrument: {
id: "U2NlbmFyadewwW9JbnN0cnVtZW50OjEz",
supplier: {
id: "U3VwcGxpZdwdwXJJbnN0cnVtZW50OjUzNjQ",
supplierDetails: {
name: "Local - Class B",
},
},
currentValue: 434323,
},
assets: {
current: 1.294353242,
fixed: 0.514434242004819,
financial: 0.07434286331588,
cash: 0.0000434398524,
alternative: 0.05242348143244779,
property: 0.543242567579,
local: 0.100432439739094,
global: 0,
},
},
],
},
];
The above data presents an array of products which consist of instruments that are described in instrumentDetails array. I am trying to find an instrument by supplier id and update its assets by multiplying all of the asset values by a given number.
Here is my function:
export const updateObject = (
productsArr: any,
supplierInstrumentId: string
) => {
return productsArr.map(
(product: any) => {
product.instrumentDetails.map(
(instrumentDetail: any) => {
if (
instrumentDetail.instrument.supplier.id ===
supplierInstrumentId
) {
instrumentDetail.assets.current = instrumentDetail.assets.current + 5;
instrumentDetail.assets.fixed= instrumentDetail.assets.fixed+ 5;
instrumentDetail.assets.financial= instrumentDetail.assets.financial+ 5;
instrumentDetail.assets.cash= instrumentDetail.assets.cash+ 5;
}
}
);
}
);
};
This function is giving an error :
Uncaught TypeError: Cannot assign to read only property 'current' of
object '#'
How can I deeply update the above data? Please help.
You need to return a new instrumentDetail-type object from the map function. Don't try to update the existing object.
(instrumentDetail: any) => {
const assets = instrumentDetail.instrument.supplier.id === supplierInstrumentId
? Object.fromEntries(
Object.entries(instrumentDetail.assets).map(([k, v]) => [k, v + 5])
)
:
instrumentDetail.assets;
return {
...instrumentDetail,
assets
};
}
Your product map is not returning which is why you're likely getting an undefined. I wasn't getting the typescript error which you mentioned above. This should leave the array in the state at which you intended.
const updateObject = (
productsArr: any,
supplierInstrumentId: string
) => {
return productsArr.map(
(product: any) => {
product.instrumentDetails.map(
(instrumentDetail: any) => {
if (
instrumentDetail.instrument.supplier.id ===
supplierInstrumentId
) {
instrumentDetail.assets.current += 5;
instrumentDetail.assets.fixed= instrumentDetail.assets.fixed+ 5;
instrumentDetail.assets.financial= instrumentDetail.assets.financial+ 5;
instrumentDetail.assets.cash= instrumentDetail.assets.cash+ 5;
return instrumentDetail;
}
}
);
return product;
}
);
};

How to fix the count variable value in a recursive method using react and javascript?

I have data like below:
const arr_obj = [
{
id: '1',
children: [],
type: 'TYPE1',
},
{
id: '2',
children: [
{
id: '1',
children: [
{
//some attributes
},
],
type: 'MAIN',
},
{
id: '2',
children: [
{
//some attributes
},
],
type: 'MAIN',
},
{
id: '3',
children: [
{
//some attributes
},
],
type: 'MAIN',
},
],
type: 'TYPE2',
},
{
id: '3',
children: [
{
id: '4',
children: [
{
//some attributes
},
],
type: 'MAIN',
},
{
id: '5',
children: [
{
//some attributes
},
],
type: 'MAIN',
},
{
id: '6',
children: [
{
//some attributes
},
],
type: 'MAIN',
},
],
type: 'TYPE2',
},
];
I have to find out the count of type: 'MAIN'. these 'MAIN' will be within type: 'TYPE2'
So the expected count is 6.
below is the code,
const ParentComponent = () => {
const findCount = (arr_obj) => {
let count = 0;
const expectedCount = 2;
const loop = (children) => {
for (const obj of children) {
const { type, children } = obj;
if (type === 'TYPE2') {
loop(children);
} else if (type === 'MAIN') {
++count;
if (count > expectedCount) return;
}
}
};
loop(children);
return count > expectedCount;
};
const output = findCount(arr_obj);
return (
//some jsx rendering
);
};
The above code works fine, but I want to make a loop (children) function a pure function. I am not sure how to do it.
The problem now is: I define variables outside the loop method.
How can I define everything as arguments to the function? You could move the function outside the component.
I have tried something like below to make it a pure function
const loop = (children, count = 0) => {
if (!children) return;
for (const obj of children) {
const { type, children } = obj;
if (type === 'TYPE2') {
loop(children, count + 1);
} else if (type === 'MAIN') {
++count;
if (count > expectedCount) return;
}
}
console.log('count', count); //this is 0 always when i log
return count;
};
const ParentComponent = () => {
const output = React.useMemo(() => {
return loop(arr_obj);
}, [arr_obj]);
console.log('output', output); // output is always false
return (
//some jsx rendering
)
};
Now the problem with above code is that the count is always 0. I am not sure where the problem is.
Your approach is fine: you update count by passing it as a parameter and by returning its updated value. However, the function returns at three spots and you only return count at the third spot. Furthermore, you call loop recursively, but there, you don't use its return value and you should pass count instead of count + 1 as an argument.
You need to make the following changes:
Inside loop, replace return; with return count; two times.
Inside loop, replace loop(children, count + 1); with count = loop(children, count);
Now, if you would remove if (count > expectedCount) return;, you would get 6 as the result.
I'm not sure what exactly you want but I don't think you'd need to make it complicated with recursion. You can simply achieve the same function like this:
const findCount = (arr) => {
const filteredArray = arr
.filter(el => el.type === 'TYPE2')
// removed all elements whose type is not TYPE2
.map(el => el.children)
// replaced all element with element's children array
.flat()
// flattened the array
.filter(el => el.type === 'MAIN');
// removed all elements whose type is not MAIN
return filteredArray.length;
};

The value of the checkbox is added to the array but the checkbox is not checked

I have checked two checkboxes. I can unchecked them, but i can't again checked them. When unchecked checkboxes, the value is removed from the array peopleChecked, when them wants to mark the value is added to the array peopleChecked, but the checkboxes aren't checked
Code here:
class App extends Component {
constructor() {
super();
this.state = {
people: [
{
firstname: "Paul",
userCompetences: [
{
asset: {
id: "12345"
}
}
]
},
{
firstname: "Victor",
userCompetences: [
{
asset: {
id: "5646535"
}
}
]
},
{
firstname: "Martin",
userCompetences: [
{
asset: {
id: "097867575675"
}
}
]
},
{
firstname: "Gregor",
userCompetences: [
{
asset: {
id: "67890"
}
}
]
}
],
peopleChecked: [
{
amount: 0,
asset: {
id: "fgfgfgfg",
name: 'Gregor'
},
asset_id: '67890'
},
{
amount: 0,
asset: {
id: "dsdsdsd"
},
asset_id: '12345'
},
],
selectPeopleId: []
}
}
handleSelect = (person) => {
//Check if clicked checkbox is already selected
var found = this.state.peopleChecked.find((element) => {
return element.asset_id === person.userCompetences[0]['asset']['id'];
});
console.log(found);
if(found){
//If clicked checkbox already selected then remove that from peopleChecked array
this.setState({
peopleChecked: this.state.peopleChecked.filter(element => element.asset_id !== person.userCompetences[0]['asset']['id']),
selectPeopleId: this.state.selectPeopleId.filter(element => element !== person.userCompetences[0]['asset']['id'])
}, () => console.log(this.state.peopleChecked))
}else{
//If clicked checkbox is not already selected then add that in peopleChecked array
this.setState({
selectPeopleId: [...this.state.selectPeopleId, person.userCompetences[0]['asset']['id']],
peopleChecked: [...this.state.peopleChecked, person]
}, () => {console.log(this.state.selectPeopleId)})
}
}
render() {
return (
<div>
{this.state.people.map(person => (
<div key={person.firstname} className="mb-1">
<input
type={'checkbox'}
id={person.id}
label={person.firstname}
checked={
this.state.peopleChecked.some(
({ asset_id }) => asset_id === person.userCompetences[0]['asset']['id']
)}
onChange={() => this.handleSelect(person)}
/> {person.firstname}
</div>
))}
</div>
);
}
}
You can probably simplify your handleSelect logic a bit. Try breaking it down so you have an array of strings to work with. This is all you need just to toggle the checkboxes:
See sandbox with working code: https://stackblitz.com/edit/react-xsqhwt?file=index.js
handleSelect = person => {
const { peopleChecked } = this.state;
let peopleCheckedClone = JSON.parse(JSON.stringify(peopleChecked));
const personId = person.userCompetences[0].asset.id;
const removeIndex = peopleChecked.findIndex(
person => person.asset_id === personId
);
if (removeIndex >= 0) {
peopleCheckedClone.splice(removeIndex, 1);
} else {
peopleCheckedClone = [...peopleCheckedClone, { asset_id: personId }];
}
this.setState({
peopleChecked: peopleCheckedClone
});
};

Change Value of Object Once Added To Array

My state looks like so:
items: [
{ id: 1, name: 'banana', price: 100, quantity: 1 },
{ id: 2, name: 'apple', price: 200, quantity: 1 },
{ id: 3, name: 'blueberry', price: 300, quantity: 1 }
]
cart: []
I have a function where I push the item to the cart:
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.indexOf(item)
if (!cart.some(x => x.id === id)) {
this.setState({
cart: [...cart, { ...item, quantity: itemQuantity }],
total: total + (price * itemQuantity)
})
}
}
I'm checking to see if the item exists before adding it to avoid duplicates. What I want to happen is if the item is already added to the cart, I want to find that object and change its quantity value.
Is this possible to do?
I think the solution below would work for you without having to update your design much. You really just have to call Array.prototype method reduce on an array of items you wish to add to the cart. Make sure to pass your cart's current state as the initial value to reduce (pass it as the second argument). https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
With some simple logic, you could just check to see if the item id already exists in the cart (which is just an object literal keyed by item id). If it doesn't, add the object and set the quantity property to 1 - if it does, just increment the quantity property by 1.
Hopefully the example below is clear and can help you out:
//an array of items we want to add to the cart
var items = [{
id: 1,
name: 'banana',
price: 100,
quantity: 1
},
{
id: 2,
name: 'apple',
price: 200,
quantity: 1
},
{
id: 3,
name: 'blueberry',
price: 300,
quantity: 1
}
];
//function utilizing reduce to update our cart object
function updateCart(cartToUpdate, itemsToAdd) {
itemsToAdd.reduce(function(cart, cartItem) {
if (!cart[cartItem.id]) {
cart[cartItem.id] = {
name: cartItem.name,
price: cartItem.price,
quantity: 1
}
} else {
cart[cartItem.id].quantity++;
}
return cart;
}, cart);
}
//implement cart as an object keyed on item id
var cart = {
1: {
name: 'banana',
price: 100,
quantity: 1
}
};
//i manually added a single banana to the cart so we can show that our update function works properly
console.log('Cart before update:');
console.log(cart);
updateCart(cart, items);
console.log('Cart after update:');
console.log(cart);
//from here it's really easy to see how many of each item we have in the cart:
Object.keys(cart).forEach(function(key) {
console.log(`The customer has ${cart[key].quantity} ${cart[key].name}(s) in their cart`)
});
//
You could do something like this:
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const index = cart.findIndex(x => x.id === id);
let quantity = itemQuantity;
const newCart = index === -1
? [...cart, {...item, quantity }]
: cart.map((it, i) => {
if (i === index) {
quantity = it.quantity + quantity;
return { ...it, quantity }
} else return it
});
this.setState({
cart: newCart,
total: total + (price * quantity)
});
}
Yes, just leverage the i const that you've already defined!
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.indexOf(item)
const newCart = [...cart]
const newTotal = total + (price * itemQuantity)
if (i !== -1) {
const newItem = { ...item, quantity: itemQuantity + 1 }
newCart[i] = newItem
this.setState({
cart: newCart,
total: newTotal,
})
return
}
newCart.push({ ...item, quantity: itemQuantity })
this.setState({
cart: newCart,
total: newTotal,
})
}
Note, it was unclear how you wanted the total functionality to work, so I've left that as it is. This, however will update the item you're looking for.
I had to check to see whether the item was added to the cart before pushing a new object with that item.
I then had to find the index of that item in the cart and update its quantity.
addItem = item => {
const { cart, total, itemQuantity } = this.state
const { price, id } = item
const i = cart.findIndex(x => x.id === id)
if (!cart.some(x => x.id === id)) {
this.setState({
cart: [
...cart,
{ ...item, quantity: itemQuantity }
]
})
} else {
this.setState({
cart: [
...cart.slice(0, i),
{ ...cart[i], quantity: cart[i].quantity + itemQuantity },
...cart.slice(i + 1)
]
})
}
this.setState({
total: total + (price * itemQuantity)
})
}

Categories

Resources