zawrdo
zawrdo

Reputation: 51

How can I pass data between sibling components in React?

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

Answers (3)

buzatto
buzatto

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

zahra zamani
zahra zamani

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

Wen W
Wen W

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

Related Questions