I'm creating a movies app with React and Redux, in each movie card I have some information about the movie like the title, image, and a button(buy a ticket). The idea is when I click on the button of each card I want to get the same image and title of the card and display it on the same page on another card that going to pop up so the user can choose the quantity and continue.
How can I get the data from the movie card onclick and transform it to another card as a pop-up?
what do you think
Single movie card Component
const SingleMovieCard = ({ id, title, poster_path, overview, toggleHandler }) => {
const [selected, isSelected] = useState(null);
return (
<article key={id} className="card">
<div key={id} onMouseEnter={() => isSelected(id)} onMouseLeave={() => isSelected(null)}>
<img src={`${ImgPath}` + poster_path} alt={title} className="image" />
{selected === id && <video src="./Trailers/SpaceJam.mp4" autoPlay={true} loop muted />}
</div>
<div className="body-card">
<h1>{title}</h1>
<p>{`${overview.substring(0, 200)}...`}</p>
</div>
<div className="services">
<FiShare2 className="icon" />
<FiHeart className="icon" />
<div className="btn-icon-container">
<BiShoppingBag className="btn-icon" />
<button onClick={() => toggleHandler()}>Buy Ticket</button>
</div>
</div>
</article>
)
}
export default SingleMovieCard;
Pop-up movie card
const PopUpMovie = ({showClass, toggleHandler}) => {
const moviesList = useSelector((state)=> state.allMovies.movies);
return (
<div className={`pop-up-container ${showClass}`}>
<nav className="pop-up">
<GrClose className="pop-up-close" onClick={()=> toggleHandler()}/>
<div className="product-details">
<div className="img-container">
<img src="./Pictures/FreeGuy.jpg" alt="FreeGuy" />
</div>
<div className="product info">
<h1 className="title">Free Guy movie</h1>
<div className="quantity">
<h4>Quantity</h4>
<span>4</span>
</div>
<h5 className="prix">11$</h5>
<button className="btn-checkout">Continue to checkout</button>
</div>
</div>
</nav>}
</div>
)
}
export default PopUpMovie;
you can use Modal from react-bootstrap
Example:
import { Modal } from "react-bootstrap";
const PopUpMovie = ({ showClass, toggleHandler }) => {
const modalContent = (
<div className={`pop-up-container ${showClass}`}>
<nav className="pop-up">
<GrClose className="pop-up-close" onClick={() => toggleHandler()} />
<div className="product-details">
<div className="img-container">
<img src="./Pictures/FreeGuy.jpg" alt="FreeGuy" />
</div>
<div className="product info">
<h1 className="title">Free Guy movie</h1>
<div className="quantity">
<h4>Quantity</h4>
<span>4</span>
</div>
<h5 className="prix">11$</h5>
<button className="btn-checkout">Continue to checkout</button>
</div>
</div>
</nav>
</div>
)
const moviesList = useSelector((state) => state.allMovies.movies);
return (
<Modal
id="order-modal-close"
backdrop="static"
show={showClass}
size={"md"}
dialogClassName="modal-90w"
onHide={toggleHandler}
>
<Modal.Header closeButton>
<Modal.Title>Movie</Modal.Title>
</Modal.Header>
<Modal.Body >{modalContent}</Modal.Body>
{modalFooter}
</Modal>
)
}
Related
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>
</>
);
};
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>
);
}
}
Im trying to make a simple fade animation when a new post is rendered
im using Firebase Firestore like this:(Feed.js)
function Feed() {
const {currentUser} = useAuth()
const [posts, setPosts] = useState([]);
useEffect(() => {
db.firestore().collection('posts').orderBy("timestamp", "desc").onSnapshot(snapshot => (
setPosts(snapshot.docs.map((doc) => ({id: doc.id, data: doc.data()
})))
))
}, []);
return <div className="feed">
{ posts.map((post,key ) => (
<Post
postwebid={key}
key={post.data.id}
profilePic={post.data.profilePic}
message={post.data.message}
username={post.data.username}
timestamp={post.data.timestamptext}
image={post.data.image}
id={post.id}
ownerID={post.data.owner}
/>
))}
</div>
}
export default Feed;
so, every time i make a new post it renders correctly as i want
the problem:
when i add an animation with spring it only displays when the app loads for the first time (fading in all the posts on screen at same time), but not when a new post comes over the screen (a single post).
im animating the posts like this: (Post.js)
function Post({profilePic,image,username,timestamp,message,id,ownerID}) {
const props = useSpring({opacity: 1, from: {opacity: 0}})
return <animated.div className="post" style={props}>
<div className="post__top">
<Avatar src={profilePic}
className="post__avatar"/>
<div className="post__topInfo">
<h3>{username}</h3>
<p>{timestamp}</p>
</div>
<div className="post__topActions">
<PostMenu id={id} ownerID={ownerID}/>
</div>
</div>
<div className="post__bottom">
<p>{message}</p>
</div>
<div className="post__image">
<img src={image} alt=""/>
</div>
<div className="post__options">
<div className="post__option"> <HeartIcon/></div>
<div className="post__option"><PurchaseIcon/></div>
</div>
</animated.div>
}
export default Post;
You can use Framer-motion or Flip move.
post.js:
const Post = forwardRef(({name,description,message,photoUrl},ref) =>{
return (
<div ref={ref} className="post">
<div className="post_header">
<Avatar src={photoUrl}>{name[0]}</Avatar>
<div className="post_info">
<h2>{name}</h2>
<p>{description}</p>
</div>
</div>
<div className="post_body">
<p>{message}</p>
</div>
<div className="post_button">
<InputOption Icon={ThumbUpOffAltIcon} title="like" color="gray"/>
<InputOption Icon={CommentOutlinedIcon} title="Comment" color="gray"/>
<InputOption Icon={ShareOutlinedIcon} title="Share" color="gray"/>
<InputOption Icon={SendOutlinedIcon} title="Send" color="gray"/>
</div>
</div>
);
})
feed.js
<FlipMove>
{posts.map( ({id,data:{name,description,message,photoUrl}}) => (
<Post
key={id}
name={name}
description={description}
message={message}
photoUrl={photoUrl}
/>
))}
</FlipMove>
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>
)
}
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>
)
}
}