React map is duplicating DOM Elements - html

I have the following issue:
I am building a web application, BackEnd in spring and FrontEnd in React.
Now I am adding a feature that shows how many products are in the cart while the client is clicking on "buy". The problem is that when I do a map to get the API in the dom tree, it seems like is duplicating the element.
Images:
Bug: "Carrinho ( )" is being duplicated
Note:
I am consuming two APIs
Code:
import React, { useEffect, useState } from 'react';
import {
Row,
CardBody,
Container,
} from 'reactstrap';
import api from '../../resources/api_produtos';
import apiCart from '../../resources/api_cart';
import axios from 'axios';
const Main = () =>{
const[product, setProduct] = useState([]);
const[cart, setCart] = useState([]);
const fetchData = () =>{
const productApi = api.get('');
const cartApi = apiCart.get('');
axios.all([productApi, cartApi]).then(
axios.spread((...allData) =>{
const allProductData = allData[0].data;
const allDataCart = allData[1].data;
setProduct(allProductData);
setCart(allDataCart);
console.log(allDataCart);
})
)
}
useEffect(() =>{
fetchData()
}, [])
return (
<div>
<div className="p-3 mb-2 bg-dark text-white d-flex justify-content-between">
<div>
<strong>Game Store</strong>
</div>
<div>
{cart.map(carrinho =>(
<div key={carrinho.id}>
<div>
Carrinho ( {carrinho.amount} ) HERE IS BEING DUPLICATED
</div>
</div>
))}
</div>
</div>
<Container>
<div className="jumbotron mt-3"><h1>Produtos</h1></div>
{product.map(produto => (
<div className="card mb-3">
<div key={produto.id}className="card-header d-flex justify-content-between">
<span>
Id: {produto.id}
</span>
<div>
<nav>
<form method="POST" action={"http://localhost:8080/comprar/" + produto.id}>
<input type="submit" className="btn btn-secondary" value="Comprar" ></input>
</form>
</nav>
</div>
</div>
<CardBody>
<Row>
<div className="col-12 col-sm-8 mb-3">
<div className="row">
<div key={produto.id}>
<div >
Nome: {produto.name}
</div>
<div >
Preço: R$ {produto.price}
</div>
</div>
</div>
</div>
<div className="col-12 col-md-4">
<figure key={produto.id}>
<img className="img-thumbnail"src={produto.image} />
</figure>
</div>
</Row>
</CardBody>
</div>
))}
</Container>
</div>
);
}
export default Main;
And this is how the API "Cart" looks like:
[{"amount":"7"},[{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"}]]
How do I fix this duplication?

The problem is the response object from your cart API.
It's bad-formed, since your BE is returning an array which gives no sense since the response includes one single cart.
So, the map function iterate over an array, modifying its content by the callback, place them in a new array, and then returns it.
So essentially you are trying to modify the obj on the index 0, which is and obj with the "amount" field, and then, you are trying to map an array at index 1.
So you have to update your response from the BE, something like that:
{
"id": "cart_id",
"items": []
}
Which has more sense compared with yours. Note that since items is an array you don't need the "amount" field, you can access it with carrinho.items.length for instance. Then render it with
<div key={carrinho.id}>
<div>Carrinho ({carrinho.items.length})</div>
</div>

Related

How can i create a dynamic page using parameter in react + Json

How can i create a dynamic page using parameter in react + Json
in my code below i was able to map the json data and i need to create a dynamic url that create the /download page with the json details of the artist when i click on "GET MP3" button.
example: When i click GET MP3 a new tab will open with a url like this https:// mysite.com/
then on the download page i can get all the details from the {data.id}.
Please any idea on how to go around it?
import React, { Component } from "react";
import {
Route,
NavLink,
HashRouter
} from "react-router-dom";
import "../customcss/style.css";
import data from "../artist/toptrending5.json";
const newdata = data.map((item) => {
const searchDownloadData = `download?id=${item.id}&artist=${item.artist}&title=${item.title}&genre=${item.genre}&urldownload=${item.urldownload}`
return (
<>
<div id="playerbackground" key= {data.id} style={{marginTop: '10px'}}>
<div className="row" align="center">
<div className="col">
<img id="Playericon" src={ Playericon } />
</div>
<div className="col ">
<button id="music-play" type="button" className="btn btn-outline-warning"><b>Play</b></button>
</div>
<div className="col-5 " align="center">
<h6 id="music-title"><b> { data.artist} - { data.title }</b></h6>
</div>
<div className="col player-col-sm-2" align="center">
<Link to={searchDownloadData} rel=" noopener noreferrer" id="music-download" className="btn btn-outline-primary"><b> MP3 </b></Link>
</div>
</div>
</div>
</>
)
}
)
export default class Top5 extends Component {
render() {
return (
<>
<h2 id="trendtop5" className="trendtop5">Top 10 Trending Bongo Music</h2>
<div id="" className="container"> {newdata} </div>
<div className="container" style={{marginTop: '10px', paddingRight: '10px' }} >
<button className="btn btn-outline-primary" ><NavLink exact to="/artist">Explore More</NavLink></button>
</div>
<br />
</>
);
}
}
Below is my router
<BrowserRouter>
<div id="wrapper">
<header id="header">
<div className="container">
<div id="logo" className="pull-left">
<img id="logo" src={ logo } />
<h1 id="h1home"><strong> Music</strong></h1>
</div>
<nav id="menu-button-nav">
<ul id="nav" className="nav-menu">
<li className="btn btn-warning"><NavLink exact to="/">Home</NavLink></li>
<li className="btn btn-warning"><NavLink to="/justin">Just In</NavLink></li>
<li className="btn btn-warning"><NavLink to="/artist">Artists</NavLink></li>
<li className="btn btn-warning"><NavLink to="/about">About</NavLink></li>
</ul>
</nav>
</div>
</header>
</div>
<div id="content" className="container" >
<Route exact path="/" component={Home}/>
<Route path="/justin" component={Justin}/>
<Route path="/artist" component={Artists}/>
<Route path="/about" component={About}/>
<Route path="/download" component={DownloadArtist}></Route></Route>
</div>
</BrowserRouter>
Below is my Json data that hold the artist details
[
{
"id":1,
"artist": "Artist 1" ,
"title": "tata",
"genre": "rock",
"urldownload" : "https://drive.google.com/uc?export=download&id=1-"
},
{
"id":2,
"artist": "Artist 1" ,
"title": "tata",
"genre": "rock",
"urldownload" : "https://drive.google.com/uc?export=download&id=1-"
},
{
"id":3,
"artist": "Artist 1" ,
"title": "tata",
"genre": "rock",
"urldownload" : "https://drive.google.com/uc?export=download&id=1-"
},
{
"id":4,
"artist": "Artist 1" ,
"title": "tata",
"genre": "rock",
"urldownload" : "https://drive.google.com/uc?export=download&id=1-"
}
]
Download Page
class DownloadArtist extends Component {
render() {
const url = new URL(window.location.href);
const id = JSON.parse(url.searchParams.get("id"));
const artist = url.searchParams.get("artist");
const title = url.searchParams.get("title");
const genre = url.searchParams.get("genre");
const urldownload = url.searchParams.get("urldownload");
console.log(url)
return (
<>
<section>
<h1 id="artistComingSoon" className="center"> ADVERT!!!</h1>
</section>
<section>
<div className="container">
<p> <strong>User ID: </strong>{id} </p>
<p> <strong>Artist Name: </strong>{artist}</p>
<p> <strong>Title: </strong>{title} </p>
<p> <strong>Genre: </strong>{genre}</p>
<p> <strong>Download Link: </strong>{urldownload}</p>
</div>
</section>
<section>
<h1 id="artistComingSoon" className="center"> ADVERT!!!</h1>
</section>
</>
);
}
}
export default DownloadArtist;
Updated
Solved
To handling URL routes in react, we are already use React Router
React Router allow us to manage routes and handling url with dynamic behavior, so that, what we need to do is:
Build route
Build url which its follow this route
For Example:
<Route path={`download/:type`}>
<myComponent />
</Route>
================
<li>
<Link to=`/download/mp3`>Download MP3</Link>
</li><li>
<Link to=`/download/video`>Download Video</Link>
</li>
If you do this, you will send url params and you can fetch it easy by this:
const {type} = useParams();
Your React app, wich is calling the download page, can pass parameter with the url. Your download page then can read those.
You can specify parameter with an "?" in your url. If you want to, you can send multiple parameter as well.
const url = `https://example.com/download/?id=${data.id}`
In your download page you can read those parameter with the URL class.
const url = new URL(window.location.href);
const id = JSON.parse(url.searchParams.get("id"));
With your example it would be something like this.
import React, { Component } from "react";
import {
Route,
NavLink,
HashRouter
} from "react-router-dom";
import "../customcss/style.css";
import data from "../artist/toptrending5.json";
const newdata = data.map((item) => {
const url = `/download/?id=${item.id}`;
return (
<>
<div id="playerbackground" key={item.id} style={{ marginTop: '10px' }}>
<div className="row" align="center">
<div className="col">
<img id="Playericon" src={Playericon} />
</div>
<div className="col ">
<button id="music-play" type="button" className="btn btn-outline-warning"><b>Play</b></button>
</div>
<div className="col-5 " align="center">
<h6 id="music-title"><b> {item.artist} - {item.title}</b></h6>
</div>
<div className="col player-col-sm-2" align="center">
<Link to={url} rel=" noopener noreferrer" id="music-download" className="btn btn-outline-primary"><b> MP3 </b></Link>
</div>
</div>
</div>
</>
)
}
)
export default class Top5 extends Component {
render() {
return (
<>
<h2 id="trendtop5" className="trendtop5">Top 10 Trending Bongo Music</h2>
<div id="" className="container"> {newdata} </div>
<div className="container" style={{ marginTop: '10px', paddingRight: '10px' }} >
<button className="btn btn-outline-primary" ><NavLink exact to="/artist">Explore More</NavLink></button>
</div>
<br />
</>
);
}
}

Ionic React too slow to render JSON file containing 100+ objects in IonCard component

I am creating a mobile app with Ionic React. I render multiple IonCards with dynamic data coming from a local JSON file. Actually, I'm mapping through the JSON file. Everything is fine. But it takes a couple of seconds to render all the cards. I want to minimize the loading time. Please help me with how do I optimize the render time and overall performance of the application. The code is below:
//imports...
import data from "../db/data.json";
const Products: React.FC = (filterText) => {
const [searchText, setSearchText] = useState("");
const categories = vocabTopics
//filtering CATEGORIES
.filter((topic) => {return topic.title.toLowerCase().indexOf(searchText.toLowerCase()) >= 0;})
.map((topic) => {
return (
<IonCol size="12" key={topic.id}>
<IonCard mode="md" routerLink={topic.route} className="except-home-screen-card no-margin no-radius-card">
<div className="flex">
<div className="card-img">
<img src={topic.thumbnail}></img>
</div>
<div className="flex-justify-space-b-w">
<div>
<IonCardSubtitle className="except-home-screen-card-subtitle">{topic.subtitle}</IonCardSubtitle>
<IonCardTitle className="except-home-screen-card-title">{topic.title}</IonCardTitle>
</div>
<div>
<IonIcon icon={chevronForwardOutline} className="card-right-icon"/>
</div>
</div>
</div>
</IonCard>
</IonCol>
);
});
return (
<IonPage>
<IonHeader className="ion-no-border">
<IonToolbar className="top-header">
<IonButtons slot="start" className="top-header-icons color-primary">
<IonBackButton defaultHref="home" /></IonButtons>
<div className="top-title-container">
<IonTitle className="ion-text-center v-center">Products</IonTitle>
</div>
</IonToolbar>
</IonHeader>
<IonContent fullscreen className="bg-style">
<div className="center padding-y">
<h1 className="lg-text ion-no-margin equal-padding">Products Categories</h>
<p className="ion-text-center ion-no-margin subtitle">70+ CATEGORIES</p>
</div>
<IonGrid className="my-grid ion-no-padding">
<IonSearchbar spellcheck={true} autocorrect="on" className="searchbar" value={searchText} mode="ios" onIonChange={(e) => setSearchText(e.detail.value!)}></IonSearchbar>
<IonRow className="center-padding">
<div className="card-container fluid">
{categories}
</div>
</IonRow>
</IonGrid>
</IonContent>
</IonPage>
);
};
export default Products;
I suppose 100 Cards are not visible at the same time in a single "view", so the only solution is the "infinite scrolling" and Display/Create them only when them should became visible. (example: https://forum.ionicframework.com/t/infinite-scrolling-of-data-from-the-api/172933)

How to display data from multiple tables in vue js and Laravel?

I'm trying to display data from three tables in my database on one page. I've managed to do it, everything is working the way I want but I have an error: Error in render: "TypeError: Cannot read property 'file' of undefined"
It seems like I'm getting this error when trying to retrieve the videos data WITHOUT using a v-for loop.
Is it possible to display data on a page without using a v-for loop?
Here is my code.
CandidateProfileController.php:
public function index()
{
$videos = Video::all();
$resumes = Resume::all();
$profile = CandidateProfile::all();
return Response::json(array(
'videos' => $videos,
'resumes' => $resumes,
'profiles' => $profile
));
}
CandidateProfileIndex.vue:
<template>
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h3 class="card-title">My Profile</h3>
<b-container class="bv-example-row">
<b-row>
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[0].file}`"
allowfullscreen
controls
></b-embed>
</b-row> <br>
<b-row>
<b-col class="text-right" cols="8"></b-col>
<b-col class="text-right" cols="2">
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[1].file}`"
controls
class="video-thumbnail"
></b-embed>
</b-col>
<b-col class="text-right" cols="2">
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[2].file}`"
controls
class="video-thumbnail"
></b-embed>
</b-col>
</b-row>
</b-container>
<br>
<b-container v-for="(profile, index) in profiles" :key="index">
<div class="b-row">
<div class="b-col" v-for="(resume, index) in resumes" :key="index">
<h4 style="float: left;">Resume:</h4>
<span style="font-size: 0.88rem;">{{resume.file}}</span><br><br>
</div>
</div>
<div class="b-row">
<div class="b-col">
<h4>Experience</h4>
<p>{{profile.experience}}</p>
</div>
</div>
<div class="b-row">
<div class="b-col">
<h4>Additional Skills</h4>
<p>{{profile.skills}}</p>
</div>
</div>
</b-container>
</div>
</div>
</div>
</template>
<script>
import * as groupedService from '../../services/grouped_data_service.js';
export default {
name: "candidateProfileIndex",
data() {
return {
profiles: [],
videos: [],
resumes: [],
};
},
mounted() {
this.loadGroupedData();
},
methods: {
loadGroupedData: async function() {
try {
const response = await groupedService.loadGroupedData();
console.log(response);
this.resumes = response.data.resumes;
this.videos = response.data.videos;
this.profiles = response.data.profiles;
console.log(this.resumes);
} catch (error) {
this.$toast.error("Some error occurred, please refresh!");
}
}
}
}
</script>
grouped_data_service.js:
import {http, httpFile} from './http_service';
export function loadGroupedData() {
return http().get('/candidate-profile');
}

Delete item in the database using a simple button on angular 7

im trying to create a delete button on one side of a word that i get from the data base and i cant figure out how to do it
I already delete the word but i have to use a input form on the html and i have to write by hand the word i that i want to delete, but this is no god for user experience, so thats why im seeking that X button
this is my html
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-body">
<h4 class="card-title">Hashtags</h4>
<h6 class="card-subtitle">Hashtags <code> uno</code> agregar.</h6>
<div class="row button-group">
<div class="col-lg-2 col-md-4" *ngFor="let hash of getHashtag">
<form [formGroup]="form" (ngSubmit)="onDelet(form.value)">
<button class="ti-close" type="submit"></button >
<input type="text" formControlName="hashtag" > {{hash}} <br>
<p id="competitors" > {{hash}}</p>
</form>
</div>
</div>
</div>
</div>
<div class="card">
this is my componet file:
public onDelet(){
this._getHashtag.deleteHashtag(this.form.value.hashtag).subscribe(
result =>{
// console.log(result)
this._getHashtag.getHashtag().subscribe(
resultado =>{
this.getHashtag = resultado
this.getHashtag = this.getHashtag.result
// console.log("Resultado", this.getHashtag)
},
error => {
var mensajeError = <any>error;
}
);
}
)
}
this is my service component:
deleteHashtag(hastagdel:string){
let header = new Headers({"Content-Type":"application/json"})
return this._http.post(this.url + "/removeHashtags" ,{hashtags:[hastagdel]}, {withCredentials:true})
}
I'm pretty sure you want to use http.delete, not http.post in your service.
http.post adds something to the db,
http.delete removes something,
http.put modifies something, and
http.get retrieves something from the db.
There are other http options, but those are the main ones.

Fetching individual JSON data on reactJS

When I parse A JSON from my server to my react front end it works fine perfectly but when I add a parameter to display an individual item I get an error. How do I display individual JSON data on react JS. I get the JSON data from my rest server. My code looks like the following.
In order to get the JSON I use the following method.
state = {
isLoading: true,
groups: [],
};
async componentDidMount() {
const response = await fetch('/product/all/1');
const body = await response.json();
this.setState({ groups: body, isLoading: false });
}
This is how I call the array
{this.state.groups.map(group => <div className="col-sm-12 col-md-6 col-lg-4 p-b-50">
{/* Block2 */}
<div className="block2">
<div className="block2-img wrap-pic-w of-hidden pos-relative block2-labelnew">
<img src={group.thumbnail} />
<div className="block2-overlay trans-0-4">
<a href="#" className="block2-btn-addwishlist hov-pointer trans-0-4">
<i className="icon-wishlist icon_heart_alt" aria-hidden="true" />
<i className="icon-wishlist icon_heart dis-none" aria-hidden="true" />
</a>
<button key={group.id} onClick={() => this.add(group.productid, group.name)} className="flex-c-m size1 bg4 bo-rad-23 hov1 s-text1 trans-0-4">Add to Cart</button>
</div>
</div>
<div className="block2-txt p-t-20">
<a href={`/productdetails/` + group.productid}>{group.name}</a>
</div>
</div>
</div>)}
I get an error saying "TypeError: this.state.groups.map is not a function"
My Spring backend to call all items and individual items look as the following
#GetMapping("/product")
public List<Product> index(){
return productRepository.findAll( );
}
#GetMapping("/product/all/{id}")
public Product show(#PathVariable String id){
int productId = Integer.parseInt(id);
return productRepository.findOne(productId);
}
P.S both api's seem working fine when tested on postman and fetching ("api/products") too works fine
this.state.groups.map is not a function . This means React expects this.state.groups to be an array. If you are returning only one item then do it like it this:
this.setState({ groups: [body], isLoading: false });
this way, body will be the first and only item in an array.