Update object value in array within array React - reactjs

I am trying to update my product attributes and i came to a solution only for one attribute.
this is my carrtItems state
this.state = {
cartItems: [],
selectedAttributes: []
Json Object
{
"__typename": "Product",
"name": "iPhone 12 Pro",
"inStock": true,
"attributes": [
{
"__typename": "AttributeSet",
"id": "Capacity",
"name": "Capacity",
"type": "text",
"items": [
{
"__typename": "Attribute",
"id": "512G",
"value": "512G"
},
{
"__typename": "Attribute",
"id": "1T",
"value": "1T"
}
]
},
{
"__typename": "AttributeSet",
"id": "Color",
"items": [
{
"__typename": "Attribute",
"id": "Black",
"value": "#000000"
},
{
"__typename": "Attribute",
"id": "White",
"value": "#FFFFFF"
}
]
}
],
"id": "product1",
"quantity": 1,
"selectedAttributes": [
{
"value": "512G",
"type": "Capacity",
"id": "product1"
},
{
"value": "#44FF03",
"type": "Color",
"id": "product1"
}
]
}
This is my update function:
updateCartItem = (cart, product, selectedAttribute, newAttributes) => {
const existingCartItem = cart.find(
(cartItem) => cartItem.id === product.id
);
const thisCart = cart.filter((cartItem) => cartItem.id !== product.id)
if (existingCartItem) {
cart.map((cartItem) =>
cartItem.selectedAttributes.map((attr)=> attr.type===selectedAttribute.map((newAttr)=>newAttr.type
? newAttributes= [{
...attr,
value: newAttr.value,
}]
: [newAttributes = attr]
)));
return [
...thisCart,
{ ...product, selectedAttributes: newAttributes },
];
}
};
This is update state function
updateItemToCart = (product, selectedAttributes) => {
this.setState({
cartItems: this.updateCartItem(this.state.cartItems, product, selectedAttributes),
});
};
This my is my selected attributes functions
selectAttribute = (attribute, newAttribute, type, id) => {
const existingAttribute = attribute.find(
(attr) => attr.type === type && attr.id === id
);
if (existingAttribute) {
return attribute.map((attr) =>
attr.type === type && attr.id === id
? { ...attr, value: newAttribute }
: attr
);
}
return [...attribute, { value: newAttribute, type, id }];
};
selectedAttributesHandler = (newAttribute, type, id) => {
this.setState({
selectedAttributes: this.selectAttribute(
this.state.selectedAttributes,
newAttribute,
type,
id
),
});
};
What i want here is to update selectedAttributes if color or size changes but to remain the other attribute.
Example:
Color: white, Size: M
Here i change the size to L on click
Result:
Color: white, Size:L
How to update updateCartItem function to achieve this result.

You can use the spread operator
It allows you to easily populate an object or array with a shallow copy of the contect of another object or array.
The syntax is three dots followed by the name of the object or array that we want to copy.
Example:
this.setState({
...this.state,
selectedAttributes: this.selectAttribute(
this.state.selectedAttributes,
newAttribute,
type,
id
),
});
There are other ways of achieving this, but this is the likely the simplest way and is a common pattern when working with state in React.

Related

Please tell me how to resolve a type error

I'm writing a program that turns two data into a new one.
Each of the data comes from an API, so the undefined case must be considered.
but,
item.id === message.sender.userId
A type error occurs in userId in the section.
I want to resolve this type error.
I want to solve it by a means that does not change the type or data.
Is this possible?
const newMessages = React.useMemo(() => {
if (messages === undefined || userItems === undefined) return [];
return messages.map(message => {
if (message.sender.type === "user" && message.sender.userId) {
const user = userItems.find(item => item.id === message.sender.userId);
if (user) message.sender = { ...message.sender, icon: user.image };
}
return message;
});
}, [messages, userItems]);
Here are the two data I'm getting, and the type of one:
type IMessage = {
sender:
| {
type: "user";
userId: number;
}
| {
type: "admin";
adminId: number;
};
body:
| {
type: "text";
text: "text";
}
| {
type: "image";
text: string;
image: string;
};
};
const messages: IMessage[] =
[
{
"sender": {
"type": "admin",
"adminId": 789
},
"body": {
"type": "image",
"text": "abcde",
"image": "https://imageUrl"
},
},
{
"sender": {
"type": "user",
"userId": 10
},
"body": {
"type": "text",
"text": "Hello!"
},
},
{
"sender": {
"type": "user",
"userId": 13
},
"body": {
"type": "image",
"text": "Hello my friend!",
"image": "https://imageUrl"
},
},
]
const userItems =
[
{
"id": 10,
"name": "kenny",
"image": "https://imageUrl",
"age": 23,
"gender": "M",
},
{
"id": 13,
"name": "Jon",
"image": "https://imageUrl",
"age": 32,
"gender": "M",
}
]
const user = userItems.find(item => item.id === message.sender.userId);
You and i know that this function is going to be called right away, but typescript does not. In general, a callback function could be called at any time, synchronously or asynchronously, and nothing in the type information specifies that. As a result, typescript cannot guarantee that message.sender will still be a user when the callback gets called.
To fix this, assign the value to a const, so typescript can know for sure it won't change.
if (message.sender.type === "user" && message.sender.userId) {
const userId = message.sender.userId
const user = userItems.find(item => item.id === userId);
// ...
P.S:
body:
| {
type: "text";
text: "text";
}
You probably meant to do text: string

Get key and value from Nested Object

I want to show the key and value from nested object with data :
const obj = {
"success": true,
"data": {
"data1": {
"label": "label1",
"value": "value1"
},
"data2": {
"label": "label2",
"value": "value2"
}
}
}
And want to show the data to object like this:
{data1: "value1", data2: "value2"}
I already try this:
const init = Object.entries(obj.data).map(([key, value]) => {
const data = `${key}: ${value.value}`;
return data;
});
But I got wrong format.
Use the reduce function
const obj = {
"success": true,
"data": {
"data1": {
"label": "label1",
"value": "value1"
},
"data2": {
"label": "label2",
"value": "value2"
}
}
}
const list = Object.keys(obj.data).reduce((acc, key) => {
acc[key] = obj.data[key].value
return acc
}, {})
console.log( list)
You could use Array#reduce for it.
const obj = {success:true,data:{data1:{label:"label1",value:"value1"},data2:{label:"label2",value:"value2"}}};
const res = Object.entries(obj.data).reduce((acc, [key, obj]) => ({
...acc,
[key]: obj.value,
}), {});
console.log(res);

How do I sort this array by date?

I'm trying to sort the dates from this external API in my latestResults array by latest on top to oldest on bottom but can't seem to figure out how.
Right now they're displayed with the oldest date first and it's working fine, but it's in the wrong order for me.
I tried using result in latestResults.reverse() but that just reverses the 7 items currently in the array.
HTML:
<div v-for="result in latestResults" v-bind:key="result.latestResults">
<small">{{ result.utcDate }}</small>
</div>
Script:
<script>
import api from '../api'
export default {
data () {
return {
latestResults: [],
limit: 7,
busy: false,
loader: false,
}
},
methods: {
loadMore() {
this.loader = true;
this.busy = true;
api.get('competitions/PL/matches?status=FINISHED')
.then(response => { const append = response.data.matches.slice(
this.latestResults.length,
this.latestResults.length + this.limit,
this.latestResults.sort((b, a) => {
return new Date(b.utcDate) - new Date(a.utcDate);
})
);
setTimeout(() => {
this.latestResults = this.latestResults.concat(append);
this.busy = false;
this.loader = false;
}, 500);
});
}
},
created() {
this.loadMore();
}
}
</script>
The JSON where I'm getting matches like this that has utcDate:
{
"count": 205,
"filters": {
"status": [
"FINISHED"
]
},
"competition": {
"id": 2021,
"area": {
"id": 2072,
"name": "England"
},
"name": "Premier League",
"code": "PL",
"plan": "TIER_ONE",
"lastUpdated": "2021-02-01T16:20:10Z"
},
"matches": [
{
"id": 303759,
"season": {
"id": 619,
"startDate": "2020-09-12",
"endDate": "2021-05-23",
"currentMatchday": 22
},
"utcDate": "2020-09-12T11:30:00Z",
"status": "FINISHED",
"matchday": 1,
"stage": "REGULAR_SEASON",
"group": "Regular Season",
"lastUpdated": "2020-09-13T00:08:13Z",
"odds": {
"msg": "Activate Odds-Package in User-Panel to retrieve odds."
},
},

arr.findIndex() returns -1

I'm afraid it should be obvious since my affair is based on normal Vanille JS. Anyhow, for any reason I cannot get my issue fixed. Anyhone who can help me out?
Snapshot of my code in reducer:
> case TOGGLE_PRODUCT:
> const newProducts = [...state.allProducts];
> console.log("All products: ", newProducts);
> console.log("Passed product: ", action.productId);
> console.log("Should have found: ", newProducts[1]);
> const toggledProduct = newProducts.findIndex(
> (el) => el.id === action.productId
> );
> console.log("Found: ", toggledProduct);
Output in console:
All products: Array [
Product {
"amount": 0,
"department": "Molkereiprodukte",
"id": "id1",
"product": "Milch 3,5%",
"status": false,
},
Product {
"amount": 0,
"department": "Molkereiprodukte",
"id": "id2",
"product": "Yoghurt",
"status": false,
},
Product {
"amount": 0,
"department": "Ceralien",
"id": "id3",
"product": "Müsli",
"status": false,
},
]
Passed product: Object {
"id": "id2",
}
Should have found: Product {
"amount": 0,
"department": "Molkereiprodukte",
"id": "id2",
"product": "Yoghurt",
"status": false,
}
Found: -1
Why does the find() method not return a result???
Thx in Advance!
your action.productId is an objectnot a string
const toggledProduct = newProducts.findIndex(
(el) => el.id === action.productId.id /** <--here */
);
findIndex is used to find desire index of the element you need Array.find to get element data. Reason you are getting -1 is because action.productId is an object. You need to compare action.productId.id
const toggledProduct = newProducts.find(el => el.id === action.productId.id );

How to search and filter in array of objects on setState

I'm trying to create a search based on an array of objects with react which data is in this format:
const data = [
{"category 1" : [
{
"name": "Orange",
"desc": "juice, orange, Water"
},
{
"name": "Ananas",
"desc": "juice, ananas, water"
}
]
},
{"category 2" : [
{
"name": "Banana Split",
"desc": "Banana, ice cream, chocolat, topping",
"allergens": "nuts"
},
{
"name": "Mango Sticky Rice",
"desc": "Mango, rice, milk",
"allergens": ""
}
]
}
]
I stored this data inside useState declaration to be able to render accordingly on data chnage:
const [filteredBySearch, setFilteredBySearch] = useState(data)
I have an input where we can type anything and set inside useState declaration.
Goal:
If I type in my input:
"Jui"
Output should be:
console.log(filteredBySearch)
/* output:
[
{"category 1" : [
{
"name": "Orange",
"desc": "juice, orange, Water"
},
{
"name": "Ananas",
"desc": "juice, ananas, water"
}
]
},
{"category 2" : []
}
]*/
Exemple 2:
If I type in my input:
"Orange banana"
Output should be:
console.log(filteredBySearch)
/* output: [
{"category 1" : [
{
"name": "Orange",
"desc": "juice, orange, Water"
}
]
},
{"category 2" : [
{
"name": "Banana Split",
"desc": "Banana, ice cream, chocolat, topping",
"allergens": "nuts"
}
]
}
]*/
I've try creating a new object with map and filter and set it with setFilteredBySearch, but I can't get anything, even creating this new object.
This the full component:
import Card from '../components/Card'
import React, { useState } from 'react';
export default function IndexPage({ data, search }) {
//search is the result of input value set on a useState
//Filter categoriesFoods by search
const [FilteredBySearch, setFilteredBySearch] = useState(data)
return (
<div className="main-content">
<div className="card-container">
{
FilteredBySearch.map(function(el, i) {
return (
<div key={i}>
<h2 className="category" id={Object.keys(el)}>{Object.keys(el)}</h2>
{
el[Object.keys(el)].map (function(itm,index){
return <Card key={index} infoItem={itm}/>
})
}
</div>
)
})
}
</div>
<style jsx>{`...`}</style>
</div>
)}
Any idea for me ?
Thanks a lot for your guidance!
I think this is what you are looking for. I have created below utilities for filtering as per your requirement.
const dataObj = [
{
'category 1': [
{
name: 'Orange',
desc: 'juice, orange, Water',
},
{
name: 'Ananas',
desc: 'juice, ananas, water',
},
],
},
{
'category 2': [
{
name: 'Banana Split',
desc: 'Banana, ice cream, chocolat, topping',
allergens: 'nuts',
},
{
name: 'Mango Sticky Rice',
desc: 'Mango, rice, milk',
allergens: '',
},
],
},
]
const checkIfInputMatches = (input, desc) => input.toLowerCase().split(" ").some(o => desc.toLowerCase().includes(o))
const filterByInput = (data, input) => {
let finalResult = [];
data.forEach(d => {
let keys = Object.keys(d);
let values = Object.values(d);
finalResult = [...finalResult, ...values.map((obj, index) => {
let result = obj.filter(o => checkIfInputMatches(input, o.desc))
return {[keys[index]]: result}
})]
})
return finalResult
}
console.log(filterByInput(dataObj, 'JUI'))
console.log(filterByInput(dataObj, "orange"))
console.log(filterByInput(dataObj, "rice"))
console.log(filterByInput(dataObj, "Orange banana"))
Hope this helps.

Resources