Reputation: 51
I am working on a React eCommerce website and I am having difficulties in showing the pdp (product detail page). The process should be that whenever a user clicks on a <Picture Card />
component, then it should open up the pdp of that product. Right now, if this happens I managed to log out the name and id of each product, but I cannot make it appear on the pdp page (I dont know how to pass the data here).
I cannot understand why setState() is not working correctly.
What am I missing ? Thanks in advance!
App.js
├── Header.js
├── Home.js
├── shop.js
├── PictureCard.js
├── Pdp.js
├── About.js
└── Footer.js
My code:
Shop.js
import React from "react"
import PictureCard from "./PictureCard"
import profile2 from "../images/profile2.jpg"
import { Link } from "react-router-dom"
import './Shop.css'
class Shop extends React.Component {
constructor() {
super()
this.state = {
productId: 1,
productTitle: "",
productPrice: "",
productDescription: "",
productImage: ""
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(id, name, price, description, image) {
console.log(id) //the id here is correct
this.setState((prevState) => {
return {
productId: prevState.productId + 1
}
})
console.log(this.state.productId) //productId doen not update
}
//console.log(id, name, price, description, image)
render() {
return (
<div className="shop-container">
<h3 className="filter-title"><Link to="/shop" className="no-dec">All pictures</Link></h3>
<div className="shop-grid">
<PictureCard
id="1"
image={profile2}
title="Strandhill Cannon Susnet"
price="20"
description="Colourful sunset at the cannon of Strandhill during lockdown"
handleClick={this.handleClick}
/>
<PictureCard
id="2"
image={profile2}
title="Bundoran in Winter"
price="20"
description="Snowy mountains view behind Bundoran colourful houses"
handleClick={this.handleClick}
/>
<PictureCard
id="3"
image={profile2}
title="Mullaghmore Runner"
price="20"
description="Being active during lockdown in County Sligo"
handleClick={this.handleClick}
/>
</div>
</div>
)
}
}
export default Shop;
PictureCard.js
import React from "react"
import "./PictureCard.css"
import { Link } from "react-router-dom"
class PictureCard extends React.Component {
constructor() {
super()
}
render() {
return(
<div className="picure-card-container" onClick={() =>
this.props.handleClick(this.props.id, this.props.title, this.props.price, this.props.description, this.props.image)}>
<Link to = {`/pdp/${this.props.title}`} className="no-dec">
<img src={this.props.image} className="picture-card-image"></img>
<h6 className="picture-card-title">{this.props.title}</h6>
<p className="picture-card-price">€ {this.props.price}</p>
</Link>
</div>
)
}
}
export default PictureCard
Upvotes: 2
Views: 4926
Reputation: 10382
you can't pass data through siblings, you need to lift up state or use other strategies. you need to find a common ancestor (in your case App.js) to place your state, and pass down your state and your setState function as props to Shop
and Pdp
. this is the simplest approach.
with a deep nested tree you would need to pass down to each component until it finds the final consumer. in this case instead you could use the Context API where you create a provider and you can consume directly.
for more complex state management (a shopping cart would fit the case) you have options like redux where you have a redux state, with reducers that handle state properly, and connect with components.
about setState:
setState is async, to print an updated state you can console.log on componentDidUpdate life cycle. also setState can take a second argument, a callback function that will run after your state's update so you can console.log your updated state.
Upvotes: 4
Reputation: 1375
setState method is an asynchronous method that's batched. This means that multiple setState
calls are batched before a component is rerendered with the new state. setState
doesn't immediately mutate the state but creates a pending state transaction.
Do this if you want to see the result of the console:
this.setState((prevState) => {
return {
...prevState,
productId: prevState.productId + 1
},() => {
console.log(this.state.productId);
});
Upvotes: 0
Reputation: 2647
setState is an async function, so that's why the console.log is not printing the correct productId.
handleClick(id, name, price, description, image) {
console.log(id) //the id here is correct
this.setState((prevState) => {
return {
productId: prevState.productId + 1
}
})
console.log(this.state.productId) //setState hasn't happened
}
if you want to see the changes, add it to componentWillReceiveProps
componentWillReceiveProps(nextProps) {
console.log(nextProps.productId);
}
Upvotes: 0