Creating carousel from JSON response - arrays

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)}
<>
})

Related

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

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

Can we add a welcome page in surveyJS library

I am using surveyJS library in my react application for creating surveys, now my requirement is to add a welcome page before questions start. can some one help me on this?
One way you can do this is by adding a page to the beginning of the survey. Then place a single HTML widget on it and add your welcome page markup to it. Here's an example:
Update: add "firstPageIsStarted": true to your survey object if showing page numbers or progress bar. See docs: https://surveyjs.io/Documentation/Library?id=surveymodel#firstPageIsStarted
{
"pages": [
{
"name": "page1",
"elements": [
{
"type": "html",
"name": "question1",
"html": "<h1>Welcome!</h1>"
}
],
"questionTitleLocation": "hidden"
},
{
"name": "page2",
"elements": [
{
"type": "text",
"name": "question2"
},
{
"type": "checkbox",
"name": "question3",
"choices": [
"item1",
"item2",
"item3"
]
}
]
}
],
"firstPageIsStarted": true
}
This will show your welcome page plus the regular SurveyJS "Next" button, as part of the survey navigation. If you don't want to use the regular navigation buttons on your welcome page you can disable it like this:
{
"name": "page1",
"elements": [
{
"type": "html",
"name": "question1",
"html": "<h1>Welcome!</h1>"
}
],
"questionTitleLocation": "hidden",
"navigationButtonsVisibility": "hide"
},
Finally you can implement your own "Start Survey" button within your welcome page markup by assigning a value to currentPageNo when the button gets clicked. For example, survey.currentPageNo = 1. Here's the documentation for it: https://surveyjs.io/Documentation/Library?id=surveymodel#currentPageNo

How to list items from object array by category and only render components based on which category was last iterated

I have made a simple webshop that I am now converting to ReactJS. There is an array of objects that fetch the products and they need to be listed by category(maximum of 3). Basically it has to go through the array and output a different section for every category(each section has its own image which I'm handling with CSS).
Then within the sections it has to show the 3 products sitting inside of that category in a col-4 bootstrap layout. The biggest issue I'm facing now is that it's showing all of the products. The idea is that while the product array is being iterated, it should keep track of the last category and the current category.
If the last iterated category is different from the current/next category, then it should render a new section element/area. Otherwise it should just place another item as a col-4 in the right place.
I want to end up with four different sections containing each 3 products from that specific category.
At the moment the product array is being mapped through with map(). It is displaying all of the products but for some reason the conditional logic I have set seems to not be working. Also it's rendering the items more than they should. So I figure that I have made some mistakes with my iterations somewhere but not sure where or what to do now.
export default class ProductTable extends React.Component {
render() {
let lastCategory = null;
const content = productItems.map((product) => (
<div className="col-4">
<div className="item-wrap">
<div className="home-prod-img-wrap">
<img src={product.img} alt="product image" className="home-prod-img" />
</div>
<div className="home-prod-title">
<h1>{product.title}</h1>
<div className="caption">
<p>{product.info}</p>
</div>
</div>
<div className="home-prod-buttons">
<div className="read-more">
discover
</div>
<div className="add-to-cart">
<i className="fas fa-shopping-cart"></i>
</div>
</div>
</div>
</div>
));
// first get the data
console.log(productCats)
const section = productItems.map((product) => (
<section key={product.id} className={"product " + (product.category === "honest skin creme" ? "first" :
product.category === "best of organic products" ? "second" :
product.category === "sprays and cremes" ? "third" :
product.category === "best uv protection" ? "fourth" : "")}>
<div className="main-title">
<h1>{product.category}</h1>
</div>
<div className="container product-container">
<div className="row">
{content}
</div>
</div>
</section>
));
return productItems.map((product) => {
if (lastCategory !== product.category) {
return (
/* go through the products array, and if the next category is not equal to the last one then render
the section part otherwise just add the content part to the mix */
<div>
{section}
</div>
)
}
return (
<div className="container product-container">
<div className="row">
{content}
</div>
</div>
);
});
}
}
And the data array:
export const productItems = [
{
"id": 1,
"category": "honest skin creme",
"title": "Smooth Cellulite Cream",
"img": "https://images.unsplash.com/photo-1562887245-9d941e87344e?ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=80",
"price": 199,
"company": "Honest Skin creme",
"info":
"this is a creme that you absolutely need in your collection... if you need to have a smooth skin. The only creme you need if you want to stay young. works for rashes but also to look way younger. like 19 if you are actually 62..",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 2,
"category": "honest skin creme",
"title": "Best Beauty Skin Creme",
"img": "https://images.pexels.com/photos/279480/pexels-photo-279480.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 249,
"company": "Honest Skin creme",
"info":
"this product makes your skin silky smooth. try it out... known for its silkness this product is the best thing for any dry part of the body. the skin is a very important part of the body and should be treated with the utmost care and respect.",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 3,
"category": "honest skin creme",
"title": "Honest Hand Creme",
"img": "https://images.pexels.com/photos/1029896/pexels-photo-1029896.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 649,
"company": "Honest Skin creme",
"info":
"keep your hands smooth and soft, take good care of the skin... everybody knows that we should always take good care of the skin. skincare is as important as coffee in the morning. thats why you should buy this product now",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 4,
"category": "best of organic products",
"title": "Organic Leaves",
"img": "https://images.pexels.com/photos/2473990/pexels-photo-2473990.jpeg?auto=compress&cs=tinysrgb&dpr=1&w=500",
"price": 49.00,
"company": "Organic Leaves Ltd.",
"info":
"use the juice in this organic leaf to appear much younger... and feel revitalized. Forever young with our organic leaves. And you can eat them too. Monkeys also like to eat our leaves.",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 5,
"category": "best of organic products",
"title": "Aloe Vera Skin Protector",
"img": "https://images.pexels.com/photos/1634502/pexels-photo-1634502.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 49.00,
"company": "Organic Leaves Ltd.",
"info":
"protect your precious skin with Aloe Vera Protector... and never have itchy skin again. This is the product most purchased by people with skin conditions that cause rashes and itches.",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 6,
"category": "best of organic products",
"title": "Hemp Oil",
"img": "https://images.pexels.com/photos/2565761/pexels-photo-2565761.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 147.20,
"company": "Organic Leaves Ltd.",
"info":
"Hemp Oil is proven to be great for the skin and your health... Hemp oil has been around since a while and for a long time civilations have successfully profited from its healing powers. this is happening more and more as well in our societies",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 7,
"category": "sprays and cremes",
"title": "Everything for the skin",
"img": "https://images.pexels.com/photos/264870/pexels-photo-264870.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 99.40,
"company": "Skin Products Ltd.",
"info":
"skin pleasure comes in our colored bottles of magic... They contain a lot of healthy organic ingredients from all over the world. Don't be a stranger and purchase some of our bottles.",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 8,
"category": "sprays and cremes",
"title": "Exotic Blue",
"img": "https://images.pexels.com/photos/1103970/pexels-photo-1103970.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 19.99,
"company": "Skin Products Ltd.",
"info":
"the No 1 item for your skin collection. get it now... Receive a special promo code when purchasing this item. dont forget to sign up for our weekly newsletter.",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 9,
"category": "sprays and cremes",
"title": "Purple Skin Powder",
"img": "https://images.pexels.com/photos/1927612/pexels-photo-1927612.jpeg?auto=compress&cs=tinysrgb&dpr=3&h=750&w=1260",
"price": 79.99,
"company": "Skin Products Ltd.",
"info":
"skin powder that keeps your skin tight and young... That's what you need. Don't listen to the competition. We are the real deal. Purchase now and don't think twice. When it comes to your skin, we know best.",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 10,
"category": "best uv protection",
"title": "Facial UV cream",
"img": "https://images.pexels.com/photos/2442898/pexels-photo-2442898.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 59.99,
"company": "Best UV",
"info":
"rub it on your skin and be 100% protected from damaging UV... when outside enjoying those summerdays. It is very important to keep from getting sunburn as the results can be quite severe. Prevent this by applying our cream every 2 hours.",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 11,
"category": "best uv protection",
"title": "Smooth Skin Spray",
"img": "https://images.pexels.com/photos/965992/pexels-photo-965992.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940",
"price": 39.99,
"company": "Best UV",
"info":
"spray this on your skin and feel ultimate bliss. it's that good... you can take our word for it. We have a special promo at the moment just use our code: SILKY4EVER123",
"inCart": false,
"count": 0,
"total": 0
},
{
"id": 12,
"category": "best uv protection",
"title": "Honest Beauty Package",
"img": "https://images.unsplash.com/photo-1562887189-4b6edf71d847?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=500&q=80",
"price": 499.99,
"company": "Best UV",
"info":
"get this promo package now and profit from a massive discount... it's almost sold out so don't wait. Great as a gift for mothers day or valentines day or just a nice surprise for that loved one.",
"inCart": false,
"count": 0,
"total": 0
}
];
The ProductTable component has the products passed to it from the Home component.
import React from 'react';
import { productItems } from '../data';
import ProductTable from './ProductTable';
export default function Home() {
return (
<div>
<ProductTable products={productItems} />
</div>
)
}
First collect all the categories and store them into a const categories.
This happens in the ProductTable Component:
import React, { Component } from 'react';
import CatSection from './CatSection';
export default class ProductTable extends Component {
render() {
const categories = this.props.products.reduce((allProducts, current) => {
return allProducts.includes(current.category) ? allProducts : allProducts.concat([current.category]);
}, []);
return (
<div>
{
categories.map((category) => {
// filter out the products of the current category
const products = this.props.products.filter(prod => prod.category === category);
// now render only those filtered out products
// and display them in the CatSection component
return (
<CatSection
products={products}
/>
)
})
}
</div>
)
}
}
Output a different section for each category (show different section depending on classname. This happens in the CatSection component
import React from 'react';
import Product from './Product';
export default function CatSection(props) {
// destructure the products
const { products } = props;
const firstProduct = products[0];
return (
<div>
<section key={firstProduct.id} className={"product " + (firstProduct.category === "honest skin creme" ? "first" :
firstProduct.category === "best of organic products" ? "second" :
firstProduct.category === "sprays and cremes" ? "third" :
firstProduct.category === "best uv protection" ? "fourth" : "")}>
<div className="main-title">
<h1>{firstProduct.category}</h1>
</div>
<div className="container product-container">
<div className="row">
{products.map(product => <Product product={product} /> )}
</div>
</div>
</section>
</div>
)
}
Explanation
In this code first the products are passed from the Home component to the ProductTable component.
In ProductTable the const categories holds all the categories. Then map() is used to iterate over the categories.
Then we filter out all the products of which the category is the same as the currently iterated category.
We pass those products to the CatSection component as a prop. The CatSection component then lists the products correctly
as they have already been filtered.
The products are then being iterated over using map(), where it uses the Product component to show that product (passing the product prop containing the actual product).

Dealing duplicate image data in React Native

I'm building 'Comments Detail page' which is a list view for comments in a single post (basically it's just facebook comments page).
I generated this JSON response data below, and as you can see, there are duplicate image urls. It means that if same user comments 100 times on a post, it needs to get image data from AWS 100 times rather than 1 time.
Maybe it's over-engineering? How do you guys deal with this?
Here is JSON data
{
"comments": [{
"id": 4,
"user": {
"image": "https://xxx.s3.amazonaws.com:443/-",
"id": 1,
"username": "jbaek73"
},
"content": "Edited!",
"publish": "2017-09-18T12:11:41.002838Z",
"updated": "2017-09-19T08:16:25.408756Z",
"reply_count": 1
},
{
"id": 13,
"user": {
"image": "https://xxx.s3.amazonaws.com:443/-",
"id": 1,
"username": "jbaek73"
},
"content": "Neaa!",
"publish": "2017-09-18T14:12:51.876523Z",
"updated": "2017-09-18T14:12:51.876600Z",
"reply_count": 0
},
{
"id": 14,
"user": {
"image": "https://xxx.s3.amazonaws.com:443/random",
"id": 5,
"username": "koreana"
},
"content": "Newa!",
"publish": "2017-09-19T08:16:35.190351Z",
"updated": "2017-09-19T08:16:35.190398Z",
"reply_count": 0
},
In this case, i would create an image object with all the required images and the user id as key:
randomFuntionName() { //you can call after you get your json
var img = []
comments.forEach((element) => { //comments are comming from your json btw
if (img[element.user.id] == null) {
img[element.user.id] = require(element.user.image)
}
})
this.setState({img})
}
render() {
//this part is only for example, you need to dynamicaly change userID
return (<Image source={this.state.img[userId]}/>)
}
This should do the work, but didn't tested it in app.

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