React - how to pass props with different names to same component - reactjs

I have a card component that receives a title, description, and image props, however the data that I receive from two different sources labels these props differently.
Source 1 (carouselContent):
[
{
"id": "1",
"title": "title 1",
"description": "Description text 1",
"image": {
"fluid": {
"src": "/get-job-you-want.jpg?w=800&q=50"
}
}
},
{
"id": "2",
"title": "title 2",
"description": "Description text 2",
"image": {
"fluid": {
"src": "/framing-a-high-deck-1.jpg?w=800&q=50"
}
}
}
]
This passed onto an <ImageSlider/> component like so:
<ImageSlider data={carouselContent} />
Then next source (relatedPrograms) looks like this:
[
{
"fullProgramName": "title 1",
"id": "1",
"metaDescription": "description 1",
"heroImage": {
"fluid": {
"src": "/denys-nevozhai-100695.jpg?w=350&h=196&q=50&fit=scale"
}
}
},
{
"fullProgramName": "title 2",
"id": "2",
"metaDescription": "description 2",
"heroImage": null
}
]
and to be called like so:
<ImageSlider data={relatedPrograms} />
How do I structure the component to be able to handle both the title and image coming from source 1 and the fullProgramName and heroImage coming from source 2?

Here's a quick suggestion on how to map your two data sources to have a common shape. Obviously you'd modify this to contain the properties that were important to your ImageSlider component that renders the images. Here I have just picked a couple fields from your example data. The important feature here is that no matter where the data come from (carousel versus related), you transform them to represent a set of images, where their origin doesn't matter and they are indistinguishable to the ImageSlider. ImageSlider probably just cares about relevant image data, so decide on a shape that represents your basic image data.
Also a codesandbox for this: https://codesandbox.io/s/dank-morning-obwld
const normalizedCarousel = carouselContent.map((item) => ({
id: item.id,
name: item.title,
src: item.image.fluid.src
}));
const normalizedRelated = relatedPrograms.map((item) => ({
id: item.id,
name: item.fullProgramName,
src: item.heroImage?.fluid.src
}));
const ImageSlider = ({ header, data }) => {
// This component now just renders a list with relevant props
// but in the real app would render the slider.
return (
<>
<h2>{header}</h2>
<ul>
{data.map((item) => {
const { id, name, src } = item;
return (
<li>
Id: {id}, Name: {name}, Src: {src}
</li>
);
})}
</ul>
</>
);
};
export default function App() {
return (
<>
<ImageSlider header="Carousel" data={normalizedCarousel} />
<ImageSlider header="Related" data={normalizedRelated} />
</>
);
}

Either you can preprocess or combine your data just before passing it as a prop to your component or you can use a secondary prop and populate only the values from your secondary data source.
const a =[
{
"id": "1",
"title": "title 1",
"description": "Description text 1",
"image": {
"fluid": {
"src": "/get-job-you-want.jpg?w=800&q=50"
}
}
},
{
"id": "2",
"title": "title 2",
"description": "Description text 2",
"image": {
"fluid": {
"src": "/framing-a-high-deck-1.jpg?w=800&q=50"
}
}
}
]
const b = [
{
"fullProgramName": "title 1",
"id": "1",
"metaDescription": "description 1",
"heroImage": {
"fluid": {
"src": "/denys-nevozhai-100695.jpg?w=350&h=196&q=50&fit=scale"
}
}
},
{
"fullProgramName": "title 2",
"id": "2",
"metaDescription": "description 2",
"heroImage": null
}
]
const c = []
a.forEach((val, idx) => {
c.push({
title: val.title,
image: val.image,
fullProgramName: b[idx].fullProgramName,
heroImage: b[idx].heroImage
});
})
Then you can easily pass that copy of data to your component

Related

Creating carousel from JSON response

I am getting a JSON response from a server which looks like this:
{
"value_1": "a",
"value_2": "b",
"pagination": {
"titles": [
"Title 1",
"Title 2",
"Title 3",
"Title 4",
"Title 5"
]
},
"slides": [
{
"pagination_id": 0,
"content": {
"heading": "Welcome!",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"next": {
"label": "Next"
}
}
},
{
"pagination_id": 1,
"content": {
"heading": "About",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"next": {
"label": "Next"
}
}
},
{
"pagination_id": 2,
"content": {
"heading": "Welcome!",
"description": "Stuff goes here",
"groups": [
{
"id": 1,
"label": "Group 1"
},
{
"id": 2,
"label": "Group 2"
},
{
"id": 3,
"label": "Group 3"
}
],
"next": {
"label": "Next"
}
}
},
{
"pagination_id": 3,
"heading": "Welcome!",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"back": {
"label": "Back"
},
"next": {
"label": "Next"
}
},
{
"pagination_id": 4,
"heading": "Welcome!",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"back": {
"label": "Back"
},
"next": {
"label": ""
}
}
],
"footer": {
"legal": {
"label": "Legal",
"url": "<url>"
},
"privacy": {
"label": "Privacy",
"url": "<url>"
},
"cookies": {
"label": "Cookies",
"url": "<url>"
}
}
}
As you can probably tell this data is being used to create a carousel with the slide content shown on each slide. The problem I am having is that the slide titles are coming from the pagination part of the JSON but the actual slide content including next and back buttons are coming from the slides part.
Currently I have some problems:
I need to get the correct title for each slide.
I need to render buttons based on the next and back properties present in each slide.
When a button is clicked to go forward or back I need to keep track of the slide that should be showing.
I already know that what I need to do for part 1 is use the pagination_id of the slide to get the correct title from the pagination.titles array but I am not entirely sure about the best way to go about this.
For the second part, I think it should be possible to also use the pagination_id to keep a track of the current slide (I think), but I am not sure how I might go about this. I should mention that the buttons for each slide are going to be render based on the next and back properties of each slide.
This application is built with React and I am currently only using local state currently as I don't think that something like Redux is really worth including for such a small amount of data.
Any help with this conundrum would be much appreciated,
Thanks for your time
In your case despite the data coming from different arrays, what you can cash around is that the number of items are the same in both arrays. So you can just use the current index of array where you are looping these items. So it would go something like this:
Updated Code
return(
data.slides.map((slide, index)=>{
return <div key={index}>
<h1> {data.pagination.titles[index]} </h1> // for title
<img src={slide.content?.image?.url || slide.image.url} alt={slide.content?.image?.alt || slide.image.alt} />
//for buttons
{(slide.content?.previous || slide.previous) && <button onClick={()=> setCurrentSlideIndex((index - 1) % data.slides.length)}> {slide.content?.previous?.label || slide.previous.label} </button>}
{(slide.content?.next || slide.next) && <button onClick={()=> setCurrentSlideIndex((index + 1) % data.slides.length)}> {slide.content?.next?.label || slide.next.label} </button>}
</div>
})
)
Hope you get the idea.
Update
However if there is lot of stuff going on then you might need to make a function which gets boolean as a parameter telling whether the particular item has content object or not, and return the ui based on that conditionally. Something like this:
const renderSlide=(content, index)=>{
if(content){
return <div>
// with slide.content.xyz
<h1> data.pagination.titles[index] </h1>
<img src={data.slides[index].content.image.url}
</div>
}
else{
return <div>
// with slide.xyz
<img src={data.slides[index].image.url}
</div>
}
}
and calling it inside your function as:
return(
data.slides.map((slide, index)=>{
<>
{renderSlide(slide.content, index)}
<>
})

Angular 5 - Dynamically add items to an array

I have these two functions:
cart() which is an array object (this information is static)
cartItems which is an array object (this information is dynamic)
Inside the cart there can be several cartItems. How do I push different items into this cart so my final result would look like this:
JSON:
"cart": [
{
"id": "1",
"cartItem": [
{
"id": "cart Item 1",
"action": "New",
"quantity": 1,
},
{
"id": "cart Item 2",
"action": "New",
"quantity": 1,
}
}
]
FUNCTIONS:
private cart(): CaCartItemsModel{
return new CaCartItemsModel(
"1",
"Cambio",
"TV"
);
private cartItems(): CaCartItemModel{
return new CaCartItemModel(
"cart Item 1/cart Item 2",
"New",
1
);
}
I managed to resolve what I needed in the following way:
private createCartItems(cancellationModel: CancellationModel) {
this.request.caAcProductOrderModel.caCartItems.getAllCartItems()
.push(this.setCartItems());
cancellationModel.channels.forEach(channel => {
this.request.caAcProductOrderModel.caCartItems.getAllCartItems()
.forEach(caCartItem => {
caCartItem.caCartItem.getAllCartItem()
.push(this.setCaCartItemModel(
channel.id,
channel.action,
channel.name,
"",
true
));
caCartItem.caCartItem.getAllCartItem()
.forEach(cartItem => {
cartItem.caProduct.caProductRelationship.getAllCaProductRelationship()
.push(this.setCaProductRelationshipModel(channel.type));
cartItem.caProductOffering.caCategory.getAllCategories()
.push(this.setCaCategoryModel("promo"));
cartItem.caProductOffering.caAttributes.getAllAttributes()
.push(this.setCaAttributesModel("Numero fijo Asociado", this.activeCustomerServiceProvider.getPhoneNumber()));
cartItem.cartItemRelationship.getAllCartItemRelationship()
.push(this.setCaCartItemRelationshipModel(channel.id, "reliesOn"));
});
});
});
}

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.

Rendering a list of nested objects in Reacts

I have a list of objects:
const products2 = [
{category: "Category 1", products:
{
product1: "1",
product2: "2"
}
},
{category: "Category 2", products:
{
product1: "3",
product2: "4"
}
},
]
How can I render it in a div?
Tried to map it but it didn't work :(
Thanks,
Kuba
products is an array so map over it should be fine. products on the other hand is not and therefore map won't work. You can use, for example, Object.values(products2[0].products) so you get 1 and 2 in an array and do what you need with it.
const products2 = [
{
category: "Category 1",
products: {
product1: "1",
product2: "2"
},
},
{
category: "Category 2",
products: {
product1: "3",
product2: "4"
},
},
]
const result = products2
.map(product => `${product.category} ${Object.values(product.products).join(' ')}`)
console.log(result)
Same applies in a react component:
render() {
return (
<div>
{products2
.map(product =>
<div>
{product.category}
{Object.values(product.products).map(name => <p>{name}</p>)}
</div>
)
}
</div>
)
}
PS: You don't need to map twice as I did. Mapping only once and returning the div right away is also fine.

How can pull values from a Json in React?

I'm trying to dosomething similar to this Angular Code but using React. I'm very new to react and can't figure it out.
I have a json that is storing data fields and a field called classes. I want to be able to pull the classes in json fields to attach them to each row. This is the angular way I have done in the past successfully.
<tr ng-repeat="row in vm.widget10.table.rows">
<td ng-repeat="cell in row">
<span class="{{cell.classes}}">
{{cell.value}}
</span>
</td>
</tr>
with a json structured this way
{
"widget10": {
"title": "Table Details",
"table": {
"columns": [{
"title": "Item Name"
},
{
"title": "Some Data"
},
{
"title": "Other Data ($)"
},
{
"title": "Visual Data (%)"
},
{
"title": "Profit/Loss ($)"
},
{
"title": "Profit/Loss (%)"
}
],
"rows": [
[{
"value": "Data Field One",
"classes": "text-boxed m-0 deep-orange-bg white-fg",
"icon": ""
},
{
"value": "$14,880.00",
"classes": "text-bold",
"icon": ""
},
{
"value": "$14,000.00",
"classes": "",
"icon": ""
},
{
"value": "%94.08",
"classes": "red-fg",
"icon": "trending_down"
},
{
"value": "$880.00",
"classes": "",
"icon": ""
},
{
"value": "%5.92",
"classes": "",
"icon": ""
}
]
]
}
}
In my react component render() I have something like this:
<TableBody
displayRowCheckbox={this.state.showCheckboxes}
deselectOnClickaway={this.state.deselectOnClickaway}
showRowHover={this.state.showRowHover}>
{statsData.map( (row, index) => (
<TableRow key={index}>
<TableRowColumn><span style={{backgroundColor:"{statsData.bgColor[index]}"}}>{row.name}</span></TableRowColumn>
<TableRowColumn>{row.data}</TableRowColumn>
<TableRowColumn>{row.o_data}</TableRowColumn>
<TableRowColumn>{row.v_data}</TableRowColumn>
<TableRowColumn>{row.o_pl}</TableRowColumn>
<TableRowColumn>{row.v_pl}</TableRowColumn>
</TableRow>
))}
</TableBody>
and a json this way (in the component)
const statsData = [
{
name: "Data Field One",
bgColor: "red",
data: "$14,880.00",
o_data: "$14,000.00",
v_data: "%94.08",
o_pl: "$880.00",
v_pl: "%5.92",
},
{
name: "Data Field Two",
bgColor: "blue",
data: "$14,880.00",
o_data: "$14,000.00",
v_data: "%94.08",
o_pl: "$880.00",
v_pl: "%5.92",
},
{
name: "Data Field Three",
bgColor: "yellow",
data: "$14,880.00",
o_data: "$14,000.00",
v_data: "%94.08",
o_pl: "$880.00",
v_pl: "%5.92",
}
];
So far the data comes through fine, but I can't figure out how to pull the bgColor as either a backgroundColor style or as a class.
Any help is appreciated.
Thanks
Remove the quotes from around the value for backgroundColor and read from the row iterator variable (based on the JSON you pasted):
<span style={{backgroundColor: row.bgColor}}>{row.name}</span>

Resources