Whenever I try to click on a button which essentially populates a modal and displays it, the whole page re-renders and everything is set back to default. I tried to use React dev tools profiler but it wasn't working. Can somebody tell me what I'm doing wrong? I haven't been able to figure out what's wrong here.
Here's my code
import React, { useState } from "react";
import Box from "#mui/material/Box";
import Modal from "#mui/material/Modal";
import "./rewards.css";
const json //some api response
const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "background.paper",
border: "2px solid #000",
boxShadow: 24,
p: 4,
};
const Rewards2 = () => {
const [showModal, setModal] = useState(false);
const [modalContent, setModalContent] = useState({
modalHeader: "",
modalImg: "",
modalContent: { content: "", terms: [], redemptionSteps: [] },
});
//const [showModal, setModal] = useState(false);
//const [s]
// onClick={handleOpen(d.name, d.img)}
const handleClose = () => setModal(false);
const handleOpen = () => setModal(true);
return (
<div id="content">
<div className="content-top">
<ul>
{json.reward_partners.map(
(partner, index) => {
// eslint-disable-next-line
return (
<div
className="row"
key={index}
>
<div className="col-md-3 col-xs-3">
<div className="widget-thumb coupon_desc">
<img
src={
partner.img
}
className="mainpnplogo1"
alt="partnership-logo"
/>
</div>
</div>
<div className="col-md-6 col-xs-6">
<div className="coupon_desc">
<div>
{
partner.content
}
</div>
</div>
</div>
<div className="col-md-3 col-xs-3">
<div className="text-center coupon_desc">
THIS IS THE BUTTON LOGIC
<a
href=""
className="theme-button theme-button-small theme-red w-btn-nocase w-btn-white knowmore-btn"
data-toggle="modal"
data-target="#couponModel"
onClick={() => {
setModalContent(
partner.modal
);
handleOpen()
console.log(
"inside onCLick",showModal
);
}}
></a>
</div>
</div>
</div>
);
}
)}
</ul>
{/* <Modal open={showModal} onClose={handleClose}>
<Box sx={style}>
<h1>{modalContent.modalHeader}</h1>
<img
src={modalContent.modalImg}
alt={modalContent.modalHeader}
/>
<p>{modalContent.modalContent.content}</p>
</Box>
</Modal> */}
<Modal
className="modal fade"
id="couponModel"
tabIndex="-1"
role="dialog"
aria-labelledby="exampleModalLabel"
open={showModal}
onClose={handleClose}
>
<Box sx = {style}>
<div
className="modal-dialog model-lg"
role="document"
>
<div
className="modal-content"
id="modalBody"
>
<div className="modal-header">
<button
type="button"
className="close"
data-dismiss="modal"
>
×
</button>
<h4 className="modal-title">
<span className="">
<b>
{
modalContent.modalHeader
}
</b>
</span>
</h4>
</div>
</div>
</div>
</Box>
</Modal>
</div>
</div>
);
};
export default Rewards2;
P.S please do ignore the redundant divs, I cut out a lot of content to show only the relevant parts.
EDIT: Highlighted the Button Logic
The issue here is with <a /> element. You can use the button instead of <a /> as on click anchor tag execute its basic behavior.
Here's a comparison between <a/> and <button /> element. Codesandbox.
export default function App() {
const handleClick = () => {
console.log("clicked");
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<a href="" onClick={handleClick}>
Anchor
</a>
<button onClick={handleClick}>button</button>
</div>
);
}
EDIT
If you have to use the <a /> element then you can add event.preventDefault() to prevent the default behaviour of <a/> tag.
Codesandbox
export default function App() {
const handleClick = (e) => {
e.preventDefault();
console.log("clicked");
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<a href="" onClick={handleClick}>
Anchor
</a>
<button onClick={handleClick}>button</button>
</div>
);
}
Related
I have list of items = exemple1, exemple2, ..... and each one clicked
when I choose one of the list the URL be like :
localhost:3000/portfolio/exemple1
localhost:3000/portfolio/exemple2
how I can do it please?
the list
import { Link } from 'react-router-dom';
export const ListPortfolio = (props) => {
const portfolio = props.portfolio;
const number = props.number;
return(
<div className="d-flex flex-wrap table">
{portfolio.map((item, i) =>
<Link to={{ pathname:"/DetailPortfolio", state:item}} state className="text-black text-decoration-none link" key={item.id} >
<div className="card">
<img src={item.image} alt={item.title} />
<div className="card-body">
<h3 className="card-title">{item.title}</h3>
<span className="url">URL: </span><span>{item.excerpt && item.excerpt.slice(10, -5)}</span>
</div>
</div>
</Link>
)}
</div>
)
}
detailsOfList
const DetailPortfolio = (props) => {
const {state} = props.location
return (
<>
<div className="container-fluid">
<Details>
<div>
<div>{state.title}</div>
<img src={state.image} alt={state.title} />
<div>{state.content}<div/>
</div>
</div>
</Details>
</div>
</>
);
}
I implemented a Card component and basically generating a bunch of cards on some input data. I binded a setter function on button click on every card which basically expands and collapse it. Even after putting unique keys to the div is sort of triggering all the cards to open at once.
Here is the code piece:
import React, { useState } from 'react';
import PrettyPrintJson from './PrettyPrintJson';
import './Card.scss';
import '../App.scss';
const Card = (props) => {
const { data } = props;
const [collapse, toggleCollapse] = useState(true);
return (<div className="card-group">
{data.map((obj, idx)=>{
return <div className="card" key={`${idx}_${obj?.lastModifiedOn}`}>
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapse(!collapse)}>Show Json</button>
</div>
</div>
<div className={`card-content ${!collapse ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>
})}
</div>
);
}
export default Card;
Create a component that manages it's own state and render that component.
const CardItem = ({ obj }) => {
const [collapse, toggleCollapse] = useState(true);
return (<div className="card">
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapse(!collapse)}>Show Json</button>
</div>
</div>
<div className={`card-content ${!collapse ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>)
}
then render it like
{data.map((obj, idx)=> (<CardItem obj={obj} key={idx} />))}
I think you can declare a state which is a type of int. After then, you can use the if-statement of index(idx) and state.
Like this:
const [collapsedCardNumbers, toggleCollapseCard] = useState([]);
const addCardNumber = (idx, prevState) => {
const arr_cardNum = prevState
!arr_cardNum .includes(idx) && arr_cardNum .push(idx)
return arr_cardNum
}
...
{data.map((obj, idx)=>{
return <div className="card" key={`${idx}_${obj?.lastModifiedOn}`}>
<div className="card-header">
<h4 className="card-title">{`fId: ${obj?.fId}`}</h4>
<h6 className="card-title">{`name: ${obj?.name}`}</h6>
<h6 className="card-title">{`status: ${obj?.status}`}</h6>
<div className="heading-elements">
<button className="btn btn-primary" onClick={() => toggleCollapseCard(prevState => addCardNumber(idx, prevState))}>Show Json</button>
</div>
</div>
<div className={`card-content ${collapsedCardNumbers.includes(idx) ? 'collapse show' : 'collapsing'}`}>
<div className="card-body">
<div className="row">
<PrettyPrintJson data={ obj } />
</div>
</div>
</div>
</div>
})}
I need to open only that modal div to which I made a click. But they all open when I click on any element.
How to make it to only that modal, to which I make a click?
App.js
import { useEffect, useState } from "react";
import Modal from "./components/Modal";
import "./style.css";
function App() {
useEffect(() => {
fetch(
"https://api.unsplash.com/photos/?client_id=cf49c08b444ff4cb9e4d126b7e9f7513ba1ee58de7906e4360afc1a33d1bf4c0"
)
.then((res) => res.json())
.then((result) => setItem(result));
}, []);
const [item, setItem] = useState([]);
const [modalActive, setModalActive] = useState(false);
return (
<div className="app">
{item.map((item) => (
<div
className="image-container"
key={item.id}
onClick={() => setModalActive(true)}
>
<img className="image" src={item.urls.regular} alt="logo" />
<div className="info">
<img
className="avatar"
src={item.user.profile_image.small}
alt="avatar"
/>
<div className="text">
<p className="name">{item.user.username}</p>
<p className="name">{item.alt_description}</p>
</div>
</div>
<Modal
active={modalActive}
setActive={setModalActive}
url={item.urls.regular}
/>
</div>
))}
</div>
);
}
export default App;
Modal.jsx
import React from "react";
import "./index.css";
const Modal = ({ setActive, active, url }) => {
return (
<div
className={active ? "modal active" : "modal"}
onClick={() => setActive(false)}
>
<div className="modal__img">
<img src={url}/>
</div>
</div>
);
};
export default Modal;
Other solution is to use the index to show or hide the modal:
<div className="app">
{item.map((item, index) => (
<div
className="image-container"
key={item.id}
onClick={() => setModalActive(index)}
>
<img className="image" src={item.urls.regular} alt="logo" />
<div className="info">
<img
className="avatar"
src={item.user.profile_image.small}
alt="avatar"
/>
<div className="text">
<p className="name">{item.user.username}</p>
<p className="name">{item.alt_description}</p>
</div>
</div>
<Modal
active={modalActive === index}
setActive={setModalActive}
url={item.urls.regular}
/>
</div>
))}
</div>
I have a problem with getting the current clicked element's data's from the API and displaying it inside React Bootstrap Modal body. The parent component is TopMovies.js which looks like:
import React, { useState } from 'react';
import TopMoviesModal from '../top-movies-modal/TopMoviesModal';
const TopMovies = ({ currentMovies }) => {
const [isOpen, setIsOpen] = useState(false);
const showModal = () => {
setIsOpen(true);
};
const hideModal = () => {
setIsOpen(false);
};
const renderMovies = () => {
if (Object.keys(currentMovies).length && currentMovies.length) {
return (
<div className="movies-labels-container">
{currentMovies.map((movie) => {
return (
<div className="movie-label"
key={movie.id.attributes['im:id']} title={movie['im:name']['label']}
onClick={showModal}>
<img className="movie-img" src={movie['im:image'][2]['label']} alt={movie['im:name']['label']} />
<br />
<div className="text-under">
<span id="movie-title">{movie['im:name']['label']}</span>
<br />
<span id="movie-subtitle">{movie.category.attributes.term}</span>
</div>
</div>
);
})}
</div>
);
}
};
return (
<section className="top-movies-list">
<div className="container-outter">
<div className="container-fluid">
<div className="row">
<div className="col-lg-12 col-md-12 col-sm-12" id="top-movies-module">
<div className="top-movies-container">
<h1 className="container-title">
TOP 100 movies
</h1>
{renderMovies()}
<TopMoviesModal
currentMovies={currentMovies}
hideModal={hideModal}
isOpen={isOpen} />
</div>
</div>
</div>
</div>
</div>
</section >
)}
export default TopMovies;
and the child component TopMoviesModal.js which looks like this:
import React from 'react';
import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/Button";
const TopMoviesModal = ({ currentMovies, hideModal, isOpen }) => {
const renderMoviesDetails = () => {
if (Object.keys(currentMovies).length && currentMovies.length) {
return (
<div>
{currentMovies.map((movie, index) => {
return (
<div className="movie-label" key={movie.id.attributes['im:id']}>
<span id="movie-title">{movie['im:name']['label']}</span>
</div>
);
})}
</div>
);
}
};
return (
<Modal
show={isOpen}
onHide={hideModal}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Modal heading
</Modal.Title>
</Modal.Header>
<Modal.Body>
{renderMoviesDetails()}
</Modal.Body>
<Modal.Footer>
<Button onClick={hideModal}>Close</Button>
</Modal.Footer>
</Modal>
);}
export default TopMoviesModal;
Could you help me to solve this problem? I would be very grateful, thanks a lot
I would like to build a simple chart in React using Nivo library to represent percentage values from data in API response.
The data from the response looks like this: https://run.mocky.io/v3/7c46c4ce-09c0-4f95-b4e1-84ae10bd24ab
I need to represent this data in a really simple line chart.
So far I have this code in my component:
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import {useParams} from 'react-router-dom';
import Loader from '../Components/Loader';
import { Bar } from "#nivo/bar";
function ArtistDetail() {
const {artist_uuid} = useParams()
const url = `https://run.mocky.io/v3/${artist_uuid}`;
const [artist, setArtist] = useState({
loading: false,
data: null,
error: false
})
let content = null;
useEffect(() => {
setArtist({
loading: true,
data: null,
error: false
})
axios.get(url)
.then(response => {
setArtist({
loading: false,
data: response.data,
error: false
})
})
.catch(() => {
setArtist({
loading: false,
data: null,
error: true
})
})
}, [url])
if(artist.error) {
content = <p>There was an error loading an artist.</p>
}
if(artist.loading) {
content = <p><Loader/></p>
}
if(artist.data) {
content =
<main className="main">
<section className="section section-artist-detail trending claimed">
<div className="page">
<div className="col visual">
<figure>
<img src= {artist.data.data.image}/>
<figcaption>
<button className="btn btn-claim-music-id">Claim music_id</button>
</figcaption>
</figure>
</div>
<div className="col-wrapper">
<div className="col info">
<div className="col-content">
<div className="info-wrapper">
<div className="title-wrapper">
<button className="btn btn-solid border btn-booking-request">Booking Request</button>
<h1 className="title">
{artist.data.data.name}
<div className="tooltip-wrapper">
<span className="profile-claimed">Profile claimed</span>
<div className="tooltip">
<h3>Vote for subgenres</h3>
<p>Don’t agree with the subgenres? Add the ones you think are missing or vote for existing to get yours on top!</p>
<div className="stats-sheet">
{artist.data.data.subgenres.map(subgenre => {
const {name, score} = subgenre;
return (
<div className="row" key={name, score}>
<h5>{name}</h5>
<div className="graph-line">
<span className="line" style= {{width: score + '%'}}>{score}%</span>
</div>
</div>
);
})}
</div>
<p>
<button className="btn btn-shadow">Vote now</button>
</p>
</div>
</div>
<span className="trending-icon">Trending</span>
</h1>
</div>
<div className="row">
<button className="btn btn-save long">Follow</button>
<button className="btn btn-share">
Share
<span>Link copied to clipboard</span>
</button>
</div>
<div className="row">
<label>Origin</label>
<a className="btn btn-filter-tag">{artist.data.data.country.name}</a>
</div>
<div className="row">
<label>Genre</label>
<span className="btn btn-filter-tag">{artist.data.data.genre.name}</span>
</div>
<div className="row">
<label>Subgenres</label>
{artist.data.data.subgenres.map(subgenre => {
const {name} = subgenre;
return (
<span key={name} className="btn btn-filter-tag">{name}</span>
);
})}
<div className="tooltip-wrapper">
<button className="btn btn-add">Add subgenre</button>
<div className="tooltip">
<h3>Vote for subgenres</h3>
<p>Don’t agree with the subgenres? Add the ones you think are missing or vote for existing to get yours on top!</p>
<div className="stats-sheet">
{artist.data.data.subgenres.map(subgenre => {
const {name, score} = subgenre;
return (
<div className="row" key={name, score}>
<h5>{name}</h5>
<div className="graph-line">
<span className="line" style= {{width: score + '%'}}>{score}%</span>
</div>
</div>
);
})}
</div>
<p>
<button className="btn btn-shadow">Vote now</button>
</p>
</div>
</div>
</div>
</div>
<div className="footer-detail">
<ul className="social-list">
{artist.data.data.social_links.map(item => {
const {channel, link} = item;
return (
<li key={channel, link}>
<a href= {link} className= {`btn social-icon ${channel}`}>{channel}</a>
</li>
);
})}
</ul>
<div className="tooltip-wrapper">
<button className="btn btn-add">Add links</button>
<div className="tooltip">
<h3>Got more info?</h3>
<p>Add Place's links so everyone can see their social media highlights.</p>
<p>
<button className="btn btn-shadow">Add links</button>
</p>
</div>
</div>
</div>
</div>
</div>
<div className="col stats">
<div className="col-content">
<Bar
width={600}
height={400}
margin={{ top: 60, right: 80, bottom: 60, left: 80 }}
data={artist.data.data.popularity}
indexBy="city"
keys={["percentage"]}
labelTextColor="inherit:darker(1.4)"
enableGridX={false}
layout="horizontal"
maxValue={10}
axisTop={null}
axisRight={null}
axisBottom={null}
axisLeft={null}
enableGridX={false}
enableGridY={false}
isInteractive={false}
/>
<div className="stats-sheet">
<label>Most popular in</label>
{artist.data.data.popularity.map(popular => {
const {city} = popular;
return (
<div className="row" key={city}>
<h5>{city}</h5>
<div className="graph-line">
<span className="line" style={{width: 47 + '%'}}>47%</span>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
<button className="btn btn-scroll-down">Scroll down</button>
</div>
</section>
</main>
}
return (
<div>
{content}
</div>
)
}
export default ArtistDetail;
I have no idea how to display the percentage data inside this div:
<div className="graph-line">
<span className="line" style={{ width: 47 + "%" }}>
47%
</span>
</div>;
It looks like this
For the chart I used the code from here
Good work!
Based on Nivo documentation, I'd try with this
<ResponsiveBar
data={data.popularity}
keys={[ 'city', 'percentage' ]}
maxValue={100}
...
>
..setting maxValue in or to 100 for percentages. Passing data.popularity or artist.data.data.popularity to data attribute and setting up appropriate keys.
{artist.data.data.popularity.map(popular => {
const {city} = popular;
Here might be the problem - destructure both 'city' and 'percentage' from 'popular'.
const { city, percentage } = popular
then
<span>{percentage}%</span>
Looking at the design instead of Nivo you can actually use simple css .Below is the code in which I have used flex to achieve that.
CodeSandbox Link
<div className="App">
<h1>Most Popular in</h1>
{popularity.map((popular) => (
<div>
<div>{popular.city}</div>
<div className="linear-graph">
<div
style={{
background: "black",
flex: popular.percentage
}}
/>
<div
style={{
background: "#c5c5c56b",
flex: 10 - popular.percentage
}}
/>
</div>
</div>
))}
</div>
and the css for that
.App {
font-family: sans-serif;
color: black;
}
.linear-graph {
width: 200px;
display: flex;
position: relative;
height: 6px;
margin-bottom: 10px;
}
Cheers