How to render popup for specific element in react? - reactjs

I have a list of items and for every item (.item-wrapper) i want to get a video popup with unique videoId.
I have prepared videoPopup component for that but for every item I get the the last 'videoId' of 'elements' array (the same videoId for every item).
On the other hand when I am not using PopupVideo component and just loop through items iframes I get the proper id for specific item - that is just for test purpose.
(The commented out line)
I am super new to React so I am aware that problem may be also super easy to solve.
Thanks!
Code for displaying items:
class Training extends Component {
constructor(props) {
super(props);
this.state = {
popupShowed: false
};
}
togglePopup = event => {
this.setState({
popupShowed: !this.state.popupShowed
});
};
onClosePopup = () => {
this.togglePopup();
};
content = () => {
const elements = ["76979871", "72675442", "337398380"];
const items = [];
for (const [index, value] of elements.entries()) {
items.push(
<div className="item-wrapper d-flex mb-4" key={index}>
<div className="item-col training-num text-white font-weight-normal d-flex align-items-center justify-content-center">
<span>{index < 10 ? "0" + index : index}</span>
</div>
<div className="item-col desc-col">
<h3 className="text-white title font-weight-normal">
Dlaczego warto?
</h3>
<div className="text-wrapper training-desc text-white">
<p>
Dowiesz się dlaczego Social Media Ninja to Zawód Przyszłości.
Dostaniesz wiedzę na temat oferowania i briefowania klientów i
dowiesz się jak zarabiać na social mediach.
</p>
</div>
</div>
<div className="item-col text-white time-col">
<div className="inside-wrapper">
<p className="text-nowrap">
<strong>Czas trwania:</strong> 2:25:00
<br />
<strong>Twój postęp:</strong> 90%
</p>
</div>
</div>
<div className="item-col play-col d-flex align-items-center justify-content-center d-flex align-items-center justify-content-center">
<div className="play-wrapper" onClick={this.togglePopup}>
<svg
enableBackground="new 0 0 60.001 60.001"
viewBox="0 0 60.001 60.001"
xmlns="http://www.w3.org/2000/svg"
className="play-button"
>
<path d="m59.895 58.531-29-58c-.34-.678-1.449-.678-1.789 0l-29 58c-.155.31-.139.678.044.973.182.294.504.474.85.474h58c.347 0 .668-.18.851-.474.182-.295.199-.663.044-.973zm-57.277-.553 27.382-54.764 27.382 54.764z" />
</svg>
<span className="text-white mt-2 d-inline-block">zobacz</span>
{/* <iframe src={'https://player.vimeo.com/video/' + value} width="500" height="600" frameBorder="0" allowFullScreen mozallowfullscreen="true" allowFullScreen></iframe> */}
</div>
</div>
{this.state.popupShowed ? (
<PopupVideo videoId={value} closePopup={this.onClosePopup} />
) : null}
</div>
);
}
return <div className="list-wrapper">{items}</div>;
};
render() {
return <Layout content={this.content()} />;
}
}
export default Training;
Code for displaying popupVideo:
class PopupVideo extends Component {
componentDidMount = () => {
var iframe = document.querySelector("iframe");
var player = new Player(iframe);
player.on("play", function() {
console.log("played the video!");
});
};
render() {
return (
<div className="popup video-popup">
<div className="popup-inner d-flex align-items-center d-flex justify-content-center">
<div className="video">
<span
onClick={this.props.closePopup}
className="close-video d-flex align-items-center justify-content-center"
>
<img
src=""
alt="close video"
/>
</span>
<div className="embed-container">
<iframe
src={
"https://player.vimeo.com/video/" +
this.props.videoId +
"?api=1&autoplay=0#t=0"
}
title="Nazwa szkolenia"
frameBorder="0"
allowFullScreen
mozallowfullscreen="true"
allowFullScreen
></iframe>
</div>
<div className="video-nav">
<div className="video-progress"></div>
<div className="d-flex align-items-center py-4">
<div className="play">
<span className="play-video"></span>
</div>
<div className="stop">
<span className="stop-video"></span>
</div>
<div className="volume">
<span className="volume-video"></span>
</div>
<div className="time">00:00 / 05:50</div>
<div className="break"></div>
<div className="title">
<h4>
<strong className="mr-3 pr-3">01</strong>Dlaczego warto ?
</h4>
</div>
<div className="button">
<button className="btn btn-secondary d-flex justify-content-center text-uppercase text-light font-weight-bold px-4">
Zobacz następny
</button>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default PopupVideo;
I do not have any error messages in the console.

Okay so i simplified the components to show you a good pattern to handle this kind of problems.
First create a VideoContainer to hold all the videoIds in the state.
We are going to return an array of Video components and pass the videoId as props to each one.
This container will be responsible to just provide the data to the other components
class VideoContainer extends Component {
state = {
videoIds: ["76979871", "72675442", "337398380"]
};
renderVideo = videoId => <Video key={videoId} videoId={videoId} />
render() {
const { videoIds } = this.state;
return videoIds.map(this.renderVideo);
}
}
Then create a Video component which will hold the popupShowed in state and will include the PopupVideo component only when popupShowed is true using && pass in the videoId and the togglePopup handler as props.
Now every Video is an independent component which hold the state for showing the PopupVideo
class Video extends Component {
state = {
popupShowed: false
};
togglePopup = () => {
this.setState(prevState => ({
popupShowed: !prevState.popupShowed
}));
};
render() {
const { popupShowed } = this.state;
const { videoId } = this.props;
return (
<div className="item-wrapper d-flex mb-4">
<button onClick={this.togglePopup}>Show Popup</button>
{popupShowed && (
<PopupVideo videoId={videoId} closePopup={this.togglePopup} />
)}
</div>
);
}
}
And last PopupVideo
const PopupVideo = ({ videoId, closePopup }) => (
<div className="popup video-popup">
<span onClick={closePopup}>
<img
src="https://rahimblak.com/images/video-close.png"
alt="close video"
/>
</span>
<div className="embed-container">
<iframe
src={
"https://player.vimeo.com/video/" +
videoId +
"?api=1&autoplay=0#t=0"
}
title="Nazwa szkolenia"
frameBorder="0"
allowFullScreen
mozallowfullscreen="true"
/>
</div>
</div>
);
sandbox

Related

how to get product id from sanity.io for every button that clicked in reactjs?

i'm trying to make product detail page but i am struggle to solve this problem. I fetch products from sanity.io and return the data using map. i want whenever i click the detail btn it's direct me to detail page and get the information of product
here is my code
const Products = () => {
const [allProducts, setAllProducts] = useState([]);
useEffect(() => {
async function getAllProducts() {
const allProduct = '*[_type == "product"]';
const response = await client.fetch(allProduct);
setAllProducts(await response);
}
getAllProducts();
}, []);
return (
<>
<Navbar />
<div className="container-fluid ">
<div className="row g-3 mb-5 ">
{allProducts.map((product) => {
console.log(product);
return (
<div className="col-12 col-md-3" key={product._id}>
<div className="card">
<img
src={urlFor(product.image[0])}
className="card-img-top"
alt={product.name}
/>
<div className="card-body">
<h1>${product.price}</h1>
<h5 className="card-title">{product.name} </h5>
<h6>{product.detail}</h6>
<div>
<Link to="/detail" className="btn btn-danger m-2">
Detail
</Link>
</div>
</div>
</div>
</div>
);
})}
</div>
<Footer />
</div>
</>
);
};

Cannot redirect one page to another page in ReactJs

I'm making a function that when i click on the image container, it will open the page with the product detail with the exact detail for each specific product. However, when i click on the image, nothing happen! Please help me to find out what wrong with my codes, thank you so much!
Product.js:
class Product extends React.Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper clasName="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<ProductContext.Consumer>
{(value) => (
<div className="img-container p-5">
<Router>
<Link to="/details">
<img
src={img}
alt="product"
className="card-img-top"
onClick={() => {
value.handleDetail(id);
}}
/>
</Link>
</Router>
<button
className="cart-btn"
onClick={() => value.addToCart(id)}
disabled={inCart ? true : false}
>
{inCart ? (
<p className="text-capitalize mb-0">In Cart</p>
) : (
<i class="fas fa-cart-plus"></i>
)}
</button>
</div>
)}
</ProductContext.Consumer>
<div className="card-footer d-flex justify-content-between">
<p className="align-self-center mb-0">{title}</p>
<h5 className="text-blue mb-0">
<span className="mr-1">$</span>
{price}
</h5>
</div>
</div>
</ProductWrapper>
);
}
}
context.js:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
handleDetail = (id) => {
const product = this.getItem(id);
this.setState(() => {
return { detailProduct: product };
});
};
addToCart = (id) => {
console.log(`hello details. id is ${id}`);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleDetail: this.handleDetail,
addToCart: this.addToCart
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
Sandbox link for better observation: https://codesandbox.io/s/why-cant-i-fetch-data-from-a-passed-value-forked-30bgi?file=/src/App.js
I recommend a different approach where the product ID goes into the URL rather than being selected in context. This has a major advantage in that refreshing the details page means the product ID will be retained.
Here is a link to a working CodeSandbox.
And here are the changes I made:
In the context provider, you can remove handleDetail since the selection will instead live in the URL:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
addToCart = (id) => {
console.log(`hello details. id is ${id}`);
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
addToCart: this.addToCart
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
In the App component, change your details route to take an itemId parameter:
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ProductProvider>
<Router>
<Switch>
<Route exact path="/productlist" component={ProductList} />
<Route path="/details/:itemId" component={Details} />
<Route path="*" component={() => "404 Not Found"} />
</Switch>
</Router>
</ProductProvider>
</div>
);
}
In your product component, Make the Link point to the details/itemId URL and remove any need to set that ID in context:
class Product extends React.Component {
render() {
const { id, title, img, price, inCart } = this.props.product;
return (
<ProductWrapper clasName="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<ProductContext.Consumer>
{(value) => (
<div className="img-container p-5">
<Link to={`/details/${id}`}>
<img src={img} alt="product" className="card-img-top" />
</Link>
<button
className="cart-btn"
onClick={() => value.addToCart(id)}
disabled={inCart ? true : false}
>
{inCart ? (
<p className="text-capitalize mb-0">In Cart</p>
) : (
<i class="fas fa-cart-plus"></i>
)}
</button>
</div>
)}
</ProductContext.Consumer>
<div className="card-footer d-flex justify-content-between">
<p className="align-self-center mb-0">{title}</p>
<h5 className="text-blue mb-0">
<span className="mr-1">$</span>
{price}
</h5>
</div>
</div>
</ProductWrapper>
);
}
}
Finally, in the Details component, pluck the itemId off of the params and find the right item from your product list in context:
class Details extends React.Component {
render() {
const { itemId } = this.props.match.params;
return (
<ProductContext.Consumer>
{(value) => {
const selected = value.products.find(
(p) => p.id === parseInt(itemId)
);
if (!selected) {
return "Bad product ID: " + itemId;
}
const { id, company, img, info, price, title, inCart } = selected;
return (
<div className="container py-5">
{/* title */}
<div className="row">
<div className="col-10 mx-auto text-center text-slanted text-blue my-5">
<h1>{title}</h1>
</div>
</div>
{/* end of title */}
<div className="row">
<div className="col-10 mx-auto col-md-6 my-3">
<img src={img} className="img-fluid" alt="" />
</div>
{/* prdoduct info */}
<div className="col-10 mx-auto col-md-6 my-3 text-capitalize">
<h1>model : {title}</h1>
<h4 className="text-title text-uppercase text-muted mt-3 mb-2">
made by : <span className="text-uppercase">{company}</span>
</h4>
<h4 className="text-blue">
<strong>
price : <span>$</span>
{price}
</strong>
</h4>
<p className="text-capitalize font-weight-bold mt-3 mb-0">
some info about product :
</p>
<p className="text-muted lead">{info}</p>
{/* buttons */}
<div>
<Link to="/productlist">
<ButtonContainer>back to products</ButtonContainer>
</Link>
<ButtonContainer
cart
disabled={inCart ? true : false}
onClick={() => {
value.addToCart(id);
}}
>
{inCart ? "in cart" : "add to cart"}
</ButtonContainer>
</div>
</div>
</div>
</div>
);
}}
</ProductContext.Consumer>
);
}
}

React Hover to conditional render component

this is my code so far
class PortfolioList extends Component{
render(){
const {column , styevariation } = this.props;
const list = PortfolioListContent.slice(0 , this.props.item);
return(
<React.Fragment>
{list.map((value , index) => (
<div className={`${column}`} key={index}>
<div className={`portfolio ${styevariation}`}>
<div className="thumbnail-inner" >
<div className={`thumbnail ${value.image}`}></div>
<div className={`bg-blr-image ${value.image}`}></div>
</div>
<div className="content" >
<div className="inner">
<p>{value.category}</p>
<h4>{value.title}</h4>
<div className="btn-container">
<div className="portfolio-button">
<a className="rn-btn" href="/portfolio-details"><FaGithub /> Git </a>
</div>
<div className="portfolio-button">
<a className="rn-btn" href="/portfolio-details"><FaExternalLinkAlt /> Live </a>
</div>
</div>
</div>
</div>
</div>
</div>
))}
</React.Fragment>
)
}
}
I want to conditionally render the div "content" and the child elements of content div when mouse is hovering over "thumbnail-inner" div. But hide content when mouse is not hovering over thumbnail-inner div.
How can i achieve this?
I didn't test this, but the idea is to add a variable in the state of the component which holds the current hovered item.
When a mouseEnter event enters to your thumbnail-inner, you update that variable with the current component index. And you set it to -1 when a mouseLeave events happens in your thumbnail-inner.
Then you simply render the content conditionally by checking if the this.state.selectedIndex === index.
class PortfolioList extends Component {
state = {
selectedItem: -1,
}
render(){
const {column , styevariation } = this.props;
const list = PortfolioListContent.slice(0 , this.props.item);
return(
<React.Fragment>
{list.map((value , index) => (
<div className={`${column}`} key={index}>
<div className={`portfolio ${styevariation}`}>
<div
className="thumbnail-inner"
onMouseEnter={ () => { this.setState({selectedItem: index}) } }
onMouseLeave={ () => { this.setState({selectedItem: -1}) } }>
<div className={`thumbnail ${value.image}`}></div>
<div className={`bg-blr-image ${value.image}`}></div>
</div>
{
this.state.selectedItem === index &&
<div className="content" >
<div className="inner">
<p>{value.category}</p>
<h4>{value.title}</h4>
<div className="btn-container">
<div className="portfolio-button">
<a className="rn-btn" href="/portfolio-details"><FaGithub /> Git </a>
</div>
<div className="portfolio-button">
<a className="rn-btn" href="/portfolio-details"><FaExternalLinkAlt /> Live </a>
</div>
</div>
</div>
</div>
}
</div>
</div>
))}
</React.Fragment>
)
}
First, you need to add state for condition of hover (ex: "onHover"), the state is for conditional rendering the <div className="content">.
Second you need create function for hover and leaveHover(onMouseEnter & onMouseLeave) we call it handleMouseEnter for onMouseEnter, and we call it handleMouseLeave for onMouseLeave.
class PortfolioList extends Component {
state = {
onHover: false,
}
handleMouseEnter() {
this.setState({onHover: true})
}
handleMouseLeave() {
this.setState({onHover: false})
}
render(){
const {column , styevariation } = this.props;
const list = PortfolioListContent.slice(0 , this.props.item);
return(
<React.Fragment>
{list.map((value , index) => (
<div className={`${column}`} key={index}>
<div className={`portfolio ${styevariation}`}>
<div
className="thumbnail-inner"
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}>
<div className={`thumbnail ${value.image}`}></div>
<div className={`bg-blr-image ${value.image}`}></div>
</div>
{
this.state.onHover &&
<div className="content" >
<div className="inner">
<p>{value.category}</p>
<h4>{value.title}</h4>
<div className="btn-container">
<div className="portfolio-button">
<a className="rn-btn" href="/portfolio-details"><FaGithub /> Git </a>
</div>
<div className="portfolio-button">
<a className="rn-btn" href="/portfolio-details"><FaExternalLinkAlt /> Live </a>
</div>
</div>
</div>
</div>
}
</div>
</div>
))}
</React.Fragment>
)
}

React Hooks - UseState - Passing object from Child to Parent

I am trying to pass data between a child and a parent component using Hooks and Functional Components.
In my parent component I have the following:-
import React, {useState} from "react"
import Card from './Card';
const CardsBoard = () => {
const { ratingObj, setRatingObj } = useState({});
console.log(ratingObj);
const cardInfo1 = {
.......
}
const cardInfo2 = {
.......
}
return (
<div className="container-fluid justify-content-center">
<div className="row">
<div className="col-md-6">
<div>
<h4>Player 1</h4>
</div>
<Card cardInfo={cardInfo1} onClick={() => setRatingObj(ratingObj)} />
</div>
<div className="col-md-6">
<h4>Player 2</h4>
<Card cardInfo={cardInfo2} onClick={() => setRatingObj(ratingObj)}/>
</div>
</div>
<div className="row top-buffer">
<div className="col-md-12 text-center">
<button id="startGameBtn" name="StartGameButton" className="btn btn-secondary">START GAME</button>
</div>
</div>
</div>
)
}
export default CardsBoard
Then in my child component I have the following:-
import React from "react"
const Card = ({ cardInfo, onClick }) => {
return (
<div className="card text-center shadow">
<h4 className="card-title">{cardInfo.title}</h4>
<div className="overflow">
<img src={cardInfo.image} alt={cardInfo.image} className="card-image" />
</div>
<div className="card-body text-dark">
<div className="container-fluid justify-content-center card-rating-text">
{
cardInfo.ratings.map(row => (
<div className="row" key={row[0].title}>
<div className="col-md-4 text-left">{row[0].title}</div>
<div className="col-md-2 text-left card-rating-color" onClick={() => onClick(cardInfo.player, row[0].title, row[0].rating)} >{row[0].rating}</div>
<div className="col-md-4 text-left">{row[1].title}</div>
<div className="col-md-2 text-left card-rating-color" onClick={() => onClick(cardInfo.player, row[1].title, row[1].rating)}>{row[1].rating}</div>
</div>
))
}
</div>
</div>
</div>
)
}
export default Card
At the moment I am getting an error:-
Uncaught TypeError: setRatingObj is not a function
at onClick (CardsBoard.js:58)
at onClick (Card.js:30)
How can I pass the onClick object from the child to the parent?
Thanks for your help and time
Change
const { ratingObj, setRatingObj } = useState({});
to
const [ ratingObj, setRatingObj ] = useState({});
useState returns a two-element array in the shape of [state, setStateFunc], so you have to apply array destructuring to it.

I had a question about passing id into my route of details when clicked

So once I click on the details page I would like to pass the id of the product to the url when you click it. So when i click the details page I would like for it to be myurl/details/itemid i found this StackOverflow answer but i cant seem to get it to work. React Router Pass Param to Component.
I would like for when my details page reloads it reloads withe correct items id.
this is my details page
import React, { Component } from "react";
import { ProductConsumer } from "../context";
import { Link } from "react-router-dom";
import { ButtonContainer } from "./Button";
import DropDown from "./Dropdown";
import ItemCategory from "./ItemCategory";
import { Carousel } from "react-responsive-carousel";
import "react-responsive-carousel/lib/styles/carousel.min.css";
import { AwesomeButton } from "react-awesome-button";
export default class Details extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.state = {
dropdownOpen: false
};
}
toggle() {
this.setState(prevState => ({
dropdownOpen: !prevState.dropdownOpen
}));
}
render() {
return (
return (
<div className="container-fluid width-100 bg-white py-5 mt-5 ">
{/* ProductInfo */}
<div className="row">
<div className="col mx-auto col-md-6 my-3 ">
<Carousel autoPlay>
<div>
<img src={img} className="img-fluid" alt="product" />
</div>
<div>
<img src={img2} className="img-fluid" alt="product" />
</div>
<div>
<img src={img3} className="img-fluid" alt="product" />
</div>
<div>
<img src={img4} className="img-fluid" alt="product" />
</div>
</Carousel>
{/* Add a Second Image */}
</div>
{/* Product Text */}
<div className="col mx-auto col-md-6 my-3 text-capitalize">
<h1 className="display-3">{title}</h1>
<h4 className="text-black">
<strong className="text-black">
price : <span>$</span>
{price}
</strong>
</h4>
<h4 className="text-blue">
</h4>
<p className="text-black ">{info}</p>
<p className="text-black ">{fabric}</p>
<small className="text-danger">{luxury}</small>
{/* buttons */}
<div>
<Link to="/all">
<AwesomeButton
className="text-capitalize mx-10"
ripple
size="large"
type="primary"
>
Back To Products
</AwesomeButton>
</Link>
<div className="mt-2">
<AwesomeButton
className="text-capitalize m-auto"
ripple
size="medium"
type="primary"
cart
disabled={inCart ? true : false}
onPress={() => {
value.addToCart(id);
}}
>
{inCart ? "inCart" : "add to cart"}
</AwesomeButton>
</div>
<ItemCategory title={category} />
<div className="mt-2">
<img
src="https://www.paypalobjects.com/digitalassets/c/website/marketing/na/us/logo-center/9_bdg_secured_by_pp_2line.png"
border="0"
alt="Secured by PayPal"
/>
</div>
</div>
</div>
</div>
</div>
);
}}
</ProductConsumer>
);
}
}
<Route path="/details/:id" component={Details} />
and in the component Details you have access
export default class Details extends Component {
render() {
return(
<div>
<h2>{this.props.match.params.id}</h2>
</div>
)
}
}

Resources