zawrdo
zawrdo

Reputation: 51

How to pass state from parent component to child component using React routes?

I am working on a React eCommerce website and I am having difficulties in showing the pdp complete with all the fields I need. 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 I get an error: src\components\Main.js Line 14:128: 'productData' is not defined no-undef

I simply want to pass the state of my <App /> component to my <Pdp /> component and to do this I have to pass trough my <Main /> component where routes is happening and creating me problems.

What am I missing ? Thanks in advance!

My code structure:

App.js
   ├── Header.js
   ├── Home.js
   ├── shop.js
            ├── PictureCard.js
   ├── Pdp.js
   ├── About.js
   └── Footer.js

My code:

App.js

import React from "react"
import Header from "./components/Header"
import Main from "./components/Main"
import Footer from "./components/Footer"

import './App.css';

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      productId: "",
      productTitle: "",
      productPrice: "",
      productDescription: "",
      productImage: ""
    }
  }

  handleCallback = (id, name, price, description, image) => {
    alert(id)
    this.setState({
      productId: id,
      productTitle: name,
      productPrice: price,
      productDescription: description,
      productImage: image
    })
  }

  render() {
    return (
      <div className="App">
          <Header />
          <Main 
            parentCallback = {this.handleCallback} 
            productData = {this.state.productPrice}
          />
          <Footer />
      </div>
    )
  }
  
}

export default App

Main.js

import React from 'react'
import { Switch, Route } from 'react-router-dom'
        
import Home from './Home'
import Shop from './Shop'
import About from './About'
import Pdp from './Pdp'
        
    function Main({parentCallback}) {
        return (
            <Switch> {/* The Switch decides which component to show based on the current URL.*/}
                <Route exact path='/' render = {(props) => (<Home parentCallback={parentCallback} {...props}/>)}/>
                <Route exact path='/shop' render = {(props) => (<Shop parentCallback={parentCallback} {...props}/>)}/>
                <Route exact path='/pdp/:productTitle' render = {(props) => (<Pdp parentCallback={parentCallback} productData={productData} {...props}/>)}/>{/*problem here! why?*/}
                <Route exact path='/about' render = {(props) => (<About parentCallback={parentCallback} {...props}/>)}/>
            </Switch>
        )
    }
        
export default Main

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.handleClick = this.handleClick.bind(this)
     }

     handleClick(id, name, price, description, image) {
          console.log(id, name, price, description, image)
          this.props.parentCallback(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}
                       />
                   
                   </div>
               </div>
           )
     }
}

export default Shop;

Pdp.js

import React from "react"

import profile2 from "../images/profile2.jpg"
import { Link } from "react-router-dom"

import './Pdp.css'

class Pdp extends React.Component {
     constructor(props) {
          super(props)
          this.state = {
               price: this.props.productData
          }
     }
     
     render() {
          return (
               <div className="pdp-page">
                   <h3 className="filter-title">
                        <Link to="/shop" className="no-dec">All pictures</Link> <span>&#8250;</span> <a href="" className="no-dec">{this.props.match.params.productTitle}</a>
                    </h3>
                   <div className="pdp-container">
                       <div>
                            <img src={this.props.image} className="pdp-image"></img>
                       </div>
                       <div className="pdp-info-container">
                            <h3 className="pdp-title">{this.props.match.params.productTitle}</h3>
                            <p className="pdp-info-paragraph">€ {this.state.price}</p>
                            <p className="pdp-info-paragraph">{this.props.description}</p>
                            <button className="purchase-button">Purchase</button>
                       </div>
                   </div>
               </div>
           )
     }
}

export default Pdp;

Upvotes: 0

Views: 138

Answers (2)

buzatto
buzatto

Reputation: 10382

as the docs points out, your render will be called with route props. given that, the props params (route props) at your function will be passed to your component, not your outside declared props. you need to provide your extra props aside to your component have access to it:

import React from 'react'
import { Switch, Route } from 'react-router-dom'
        
import Home from './Home'
import Shop from './Shop'
import About from './About'
import Pdp from './Pdp'
        
    function Main(props) {
        return (
            <Switch> {/* The Switch decides which component to show based on the current URL.*/}
                <Route exact path='/' render = {(routeProps) => (<Home {...props} {...routeProps} />)}/>
                <Route exact path='/shop' render = {(routeProps) => (<Shop {...props} {...routeProps} />)}/>
                <Route exact path='/pdp/:productTitle' render = {(routeProps) => (<Pdp {...props} {...routeProps} />)}/>
                <Route exact path='/about' render = {(routeProps) => (<About {...props} {...routeProps} />)}/>
            </Switch>
        )
    }
        
export default Main

fwiw since the props you're passing has the same name as the prop at your component you don't need to destructure it, you could only spread your props directly

Upvotes: 0

Taghi Khavari
Taghi Khavari

Reputation: 6582

you need to define the props and the destructure it if you're using it directly:

    function Main(props) {
        const {parentCallback} = props;
        return (
            <Switch> {/* The Switch decides which component to show based on the current URL.*/}
                <Route exact path='/' render = {(props) => (<Home parentCallback={parentCallback} {...props}/>)}/>
                <Route exact path='/shop' render = {(props) => (<Shop parentCallback={parentCallback} {...props}/>)}/>
                <Route exact path='/pdp/:productTitle' render = {(props) => (<Pdp parentCallback={parentCallback} productData={productData} {...props}/>)}/>{/*problem here! why?*/}
                <Route exact path='/about' render = {(props) => (<About parentCallback={parentCallback} {...props}/>)}/>
            </Switch>
        )
    }

Upvotes: 1

Related Questions