I have state data like that:
const IssuesList = () => {
const [issuesList, setIssuesList] = useState([
{
groupId: 0,
groupData: "19-07-2016",
groupIssues: [
{
id: 0,
title: "Page changes",
star: true,
},
{
id: 1,
title: "Review of last issues",
star: true,
},
],
},
{
groupId: 1,
groupData: "18-07-2016",
groupIssues: [
{
id: 2,
title: "Visual UI Update Review",
star: false,
},
{
id: 3,
title: "Sidebar changes",
star: false,
},
],
},
{
groupId: 2,
groupData: "15-07-2016",
groupIssues: [
{
id: 4,
title: "Crash update",
star: false,
},
{
id: 5,
title: "Page visual UI Update Review",
star: true,
},
{
id: 6,
title: "Sidebar update",
star: false,
},
],
},
{
groupId: 3,
groupData: "14-07-2016",
groupIssues: [
{
id: 7,
title: "Crash issue",
star: true,
},
{
id: 8,
title: "Visual update & Crash resolve",
star: true,
},
{
id: 9,
title: "Header changes",
star: false,
},
],
},
]);
return (
<div className="issuesList">
{issuesList.map((group) => (
<IssueGroup
key={group.groupId}
group={group}
setIssuesList={setIssuesList}
issuesList={issuesList}
/>
))}
</div>
);
};
export default IssuesList;
I want to change only the star parameter in groupIssues when I click an specyfic icon in a StarIcon component:
const StarIcon = ({ star, index, id, setIssuesList, issuesList, group }) => {
const issueStar = group.groupIssues[index].star;
return (
<svg
xmlns="http://www.w3.org/2000/svg"
//xmlns:xlink="http://www.w3.org/1999/xlink"
preserveAspectRatio="xMidYMid"
width="17"
height="16"
viewBox="0 0 17 16"
className={star ? "staricon filled" : "staricon unfilled"}
onClick={() => {
// setIssuesList() TODO
}}
>
<path
d="M8.500,0.000 L11.301,5.028 L16.999,6.112 L13.033,10.302 L13.753,16.000 L8.500,13.561 L3.247,16.000 L3.967,10.302 L0.001,6.112 L5.699,5.028 L8.500,0.000"
fill={star ? "#21233d" : "#fff"}
stroke={star ? "none" : "#e0e0e0"}
/>
</svg>
);
};
export default StarIcon;
What is the best way to do that in that set of data. I know that spread operator can be helpfull there but i have no idea how to implement that in this data structure when We have array of objects and then we need to get into another array in specyfic object. Maybe changing the data structure will be better or breaking it into two arrays?
Ui view here
You should use a deep copy of the object, spread operator creates only a shallow copy.
Example:
const changeStar = (grpId, issueId) => {
setIssuesList(list => (
list.map(item => (
item.groupId === grpId ?
{...item, groupIssues: item.groupIssues.map(issue => (
issue.id === issueId ? {...issue, star: !issue.star} : issue
))}
: item
)
))
}
Explanation:
map function creates an entirely new array. It will return the elements as it is unless you have matching groupIds. Inside the groupIds, you will change the item based on the id. In the end, you deep copied the object and set an entirely new array with the desired result.
I think you need something like this: copy of current issuesList state. Update the specific star from that copy of state. Set the updated state.
Here is a working sandbox example: https://codesandbox.io/s/bold-browser-cnfw3?file=/src/App.js
Related
I'm trying to achieve the following:
For each object in addedExtra, if the field applicableOnProduct is empty, then I still want it to be rendered on the page however lets say an object that does have content in applicableOnProduct (length > 0) then it's only where I want the .filter and .map check to happen.
How do I do this in React?
const addedExtra = [{
name: 'Bed Sheet',
applicableOnProduct: [],
selected: false
},
{
name: 'Pillows',
applicableOnProduct: [],
selected: false
},
{
name: 'Luxury Bet Set',
applicableOnProduct: [{
title: 'Luxury Bed',
productId: '857493859049'
}],
selected: false
},
];
return (
{addedExtra
.filter(({ applicableOnProduct}) =>
applicableOnProduct.some(({ productId}) => productId === product.id)
)
.map((extra) => {
return <Extra {...extra}></Extra>;
})
}
)
I want to add a conditional object inside an array of objects. If the condition is not met, I want it as if that object is not there AT ALL, while keeping the other objects as they are.
Consider the following:
const CardBuildingBlock: FC = () => {
const type = 'typeA';
const typesOfCards = [
{name: 'Card A'
size: 'Medium'
action: 'make'},
{name: 'Card B'
size: 'Small'
action: 'break'},
{name: 'Card C'
size: 'Large'
action: 'build'},
//I tried doing the following but it doesn't work
type == 'typeA' ? null : {
name: 'Card A'
size: 'Medium'
action: 'make'},
];
return(
typeOfCards.map(({name, size, action}) => (
<BuildCard
name = {name}
size = {size}
action = {action}
/>
)
)};
Please Help.!!!
Thanks for the help.
From what I understood, you want to filter away all the elements of the array given a condition. What I would do is adding a new key to the object specifying if it should be displayed, and then filter & map.
const typesOfCards = [
{ name: "Card A", size: "Medium", action: "make", type: "typeA" },
...
];
return typesOfCards.filter(card => card.type === "typeA").map(({ name, size, action }) => (
<BuildCard name={name} size={size} action={action} />
));
Easiest way here would be to concat new element to an array. In case condition is true, you will concat new element. Consider this examples:
// using if statement
const type = "typeA";
let additionalCardOfTypeA = {
name: "Card A",
size: "Medium",
action: "make",
};
let typesOfCards = [
{ name: "Card A", size: "Medium", action: "make" },
{ name: "Card B", size: "Small", action: "break" },
{ name: "Card C", size: "Large", action: "build" },
];
if (type === "typeA") {
typesOfCards = typesOfCards.concat(additionalCardOfTypeA);
}
// using ternary operator
const type = "typeA";
let additionalCardOfTypeA = {
name: "Card A",
size: "Medium",
action: "make",
};
let typesOfCards = [
{ name: "Card A", size: "Medium", action: "make" },
{ name: "Card B", size: "Small", action: "break" },
{ name: "Card C", size: "Large", action: "build" },
].concat(
type === "typeA"
? additionalCardOfTypeA
: []
);
Edit
To insert new element in particular place you will have to create additional arrays. First, find a place for your element. Then, create an array that have everything before said index in original, and array that have everything from index to an end. Then concatenate start, new element and end into final array.
const type = "typeA";
let additionalCardOfTypeA = {
name: "Card A",
size: "Medium",
action: "make",
};
let typesOfCards = [
{ name: "Card A", size: "Medium", action: "make" },
{ name: "Card B", size: "Small", action: "break" },
{ name: "Card C", size: "Large", action: "build" },
];
if (type === "typeA") {
let indexForNewElement = getSomehowIndex();
// Getting everything before index
let head = typesOfCards.slice(0, indexForNewElement);
// Getting everything after index
let tail = typesOfCards.slice(indexForNewElement);
typesOfCards = head.concat(additionalCardOfTypeA).concat(tail);
}
Maybe a push would be useful in that case:
type === 'typeA' && typesOfCards.push({
name: 'Card A'
size: 'Medium'
action: 'make'}
)
maybe you might want to include that within a function and it should return the typesOfCards array
Is there a solution to check in JavaScript if two objects have the same value and then get the value?
Here is some code for demonstration:
An Object in Array 1 (“generalData”):
this.generalData = [
{
specificId: 210001,
name: 'Test 1',
optionsAvaiable: false,
mode: 0,
},
];
An Object in Array 2 (“selectedData”):
this.selectedData = [
{
specificId: 210001,
name: 'Test 1',
optionsAvaiable: false,
column: {
disableHtmlEncode: true,
allowSorting: false,
allowResizing: true,
allowFiltering: false,
allowGrouping: false,
},
foreignKeyData: undefined,
index: '0',
mode: 0,
},
];
I want to get the specific id so the expected output is 210001
My current solution (messy):
this.generalData.forEach((generalDataObj) => {
this.selectedData.forEach((selectedDataObj) => {
if (generalDataObj.specificId == selectedDataObj.specificId) {
console.log(generalDataObj.specificId); // (210001)
}
});
});
Is there a cleaner solution to solve this?
If you only want one result, you can use the find method.
const generalData = [{ specificId: 2345 }, { specificId: 1234 }]
const selectedData = [{ specificId: 1234 }, { specificId: 876 }, { specificId: 675 }]
const result = generalData.find(a => selectedData.find(b => a.specificId === b.specificId))
console.log(result)
I'm trying to render menu values from a JSON. Considering that this is a multilevel menu, I'm trying to do a nested map to render the full menu.
const menu = {
data:[
{
title: "Home",
child: [
{
title: "SubLevel1",
child: {
title: "SubSubLevel1"
}
},
{
title: "SubLevel2",
child: [
{title: "SubSubLevel1"},
{title: "SubSubLevel2"}
]
}
]
},
{
title: "About",
},
{
title: "Contact",
}
]}
And here is the part when I use the map function :
const MenuNavigation = () => {
return (
{menu.data.map((item) => (
<div>
<ul>{item.title}</ul>
{item.map((sub, id) =>
<li>{sub.child[id].title}</li>
)}
</div>
))}
)
};
I managed to render main level for the menu (Home, About, Contact), but how can I print sublevel & subsublevel values?
Another question: Is there a way to map recursively a tree structure?
Try below:
The menu should be like this. Your menu had one issue in child of "SubLevel1" not defined inside an array.
const menu = {
data: [
{
title: "Home",
child: [
{
title: "SubLevel1",
child: [{
title: "SubSubLevel1",
}],
},
{
title: "SubLevel2",
child: [{ title: "SubSubLevel1" }, { title: "SubSubLevel2" }],
},
],
},
{
title: "About",
},
{
title: "Contact",
},
],
};
Render it recursively like below. I have added a margin-left to see the levels properly.
const renderMenu = (menu) => {
return menu.map((item) => (
<div style={{ marginLeft: '25px' }}>
{item.title}
{item.child && renderMenu(item.child)}
</div>
))
}
return <div>{renderMenu(menu.data)}</div>;
How can I map movies by using columns as a property reference.
Like
{movies.map(item => {columns.map(column => item.column.path)})}
but using this i'm getting result as undefined
Movies contains all details about movies
const movies = [
{
_id: "5b21ca3eeb7f6fbccd471815",
title: "Terminator",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 6,
dailyRentalRate: 2.5,
publishDate: "2018-01-03T19:04:28.809Z",
liked: true,
},
{
_id: "5b21ca3eeb7f6fbccd471816",
title: "Die Hard",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 5,
dailyRentalRate: 2.5,
},
{
_id: "5b21ca3eeb7f6fbccd471817",
title: "Get Out",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 8,
dailyRentalRate: 3.5,
},
{
_id: "5b21ca3eeb7f6fbccd471819",
title: "Trip to Italy",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5,
},
{
_id: "5b21ca3eeb7f6fbccd47181a",
title: "Airplane",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5,
}];
Columns contains all properties that are needed to access the movies
const columns = [
{ path: "title", label: "Title" },
{ path: "genre", label: "Genre" },
{ path: "numberInStock", label: "Stock" },
{ path: "dailyRentalRate", label: "Rate" }];
I know this problem can be solved using 2 loops.
1 outer loop for movies after getting each movie, use a 2nd internal loop to access the properties
Update: After getting #rory-mccrossan answer
Further, how can I map this data in a table such that
This is the code part regarding the same that I used.
import React, { Component } from "react";
import _ from "lodash";
class TableBody extends Component {
renderCell = (item, column) => {
if (column.content) return column.content(item);
return _.get(item, column.path);
};
createKey = (item, column) => {
return item._id + (column.path || column.key);
};
render() {
const { data, columns } = this.props;
return (
<tbody>
{data.map((item) => (
<tr key={item._id}>
{columns.map((column) => (
<td key={this.createKey(item, column)}>
{this.renderCell(item, column)}
</td>
))}
</tr>
))}
</tbody>
);
}
}
export default TableBody;
But I'm getting the error: Objects are not valid as a React child (found: object with keys {_id, name}). If you meant to render a collection of children, use an array instead.)
The main issue is that how can I map data in a table
There's two issues here. Firstly the braces around the second map() call will be interpreted as a code block, yet it does not return any value so the resulting array will be empty. Remove those braces.
Secondly, item.column.path needs to be item[column.path] as you're using column.path as the property accessor of item.
Here's a working example:
const movies = [{_id:"5b21ca3eeb7f6fbccd471815",title:"Terminator",genre:{_id:"5b21ca3eeb7f6fbccd471818",name:"Action"},numberInStock:6,dailyRentalRate:2.5,publishDate:"2018-01-03T19:04:28.809Z",liked:!0},{_id:"5b21ca3eeb7f6fbccd471816",title:"Die Hard",genre:{_id:"5b21ca3eeb7f6fbccd471818",name:"Action"},numberInStock:5,dailyRentalRate:2.5},{_id:"5b21ca3eeb7f6fbccd471817",title:"Get Out",genre:{_id:"5b21ca3eeb7f6fbccd471820",name:"Thriller"},numberInStock:8,dailyRentalRate:3.5},{_id:"5b21ca3eeb7f6fbccd471819",title:"Trip to Italy",genre:{_id:"5b21ca3eeb7f6fbccd471814",name:"Comedy"},numberInStock:7,dailyRentalRate:3.5},{_id:"5b21ca3eeb7f6fbccd47181a",title:"Airplane",genre:{_id:"5b21ca3eeb7f6fbccd471814",name:"Comedy"},numberInStock:7,dailyRentalRate:3.5}];
const columns = [{path:"title",label:"Title"},{path:"genre",label:"Genre"},{path:"numberInStock",label:"Stock"},{path:"dailyRentalRate",label:"Rate"}];
let output = movies.map(item => columns.map(column => item[column.path]));
console.log(output);