jonlastthelast
jonlastthelast

Reputation: 35

React onClick event in array.map()

Edit: I have included the full code for better clarity

I am not sure if this is possible. I am trying to pass an onClick event via props but the click event does not work.

The parent component looks like this:

import React from 'react'
import { getProductsById } from '../api/get'
import Product from './Product'
import { instanceOf } from 'prop-types'
import { withCookies, Cookies } from 'react-cookie'

class Cart extends React.Component {
    static propTypes = {
        cookies: instanceOf(Cookies).isRequired
    }
    constructor(props) {
        super(props)
         const { cookies } = props;
            this.state = {
            prods: cookies.get('uircartprods') || '',
            collapsed: true,
            total: 0,
        }
        this.expand = this.expand.bind(this)
        this.p = [];
    }
    componentDidMount() {
        this.getCartProducts()
    }
    handleClick = (o) => {
        this.p.push(o.id)
        const { cookies } = this.props
        cookies.set('uircartprods', this.p.toString(), { path: '/' , sameSite: true})
        this.setState({prods: this.p })
        console.log('click')
    }
    getCartProducts = async () => {
        let products = []
        if (this.state.prods !== '') {
            const ts = this.state.prods.split(',')
            for (var x = 0; x < ts.length; x++) {
                var p = await getProductsById(ts[x])
                var importedProducts = JSON.parse(p)
                importedProducts.map(product => {
                    const prod = <Product key={product.id} product={product} handler={() => this.handleClick(product)} />
                    products.push(prod)
                })
            }
            this.setState({products: products})
        }
    }
    expand(event) {
        this.setState({collapsed: !this.state.collapsed})
    }
    handleCheckout() {
        console.log('checkout clicked')
    }
  render() {
    return (
            <div className={this.state.collapsed ? 'collapsed' : ''}>
                <h6>Your cart</h6>
                <p className={this.state.prods.length ? 'hidden' : ''}>Your cart is empty</p>
                {this.state.products}
                <h6>Total: {this.props.total}</h6>
                <button onClick={this.handleCheckout} className={this.state.prods.length ? '' : 'hidden' }>Checkout</button>
                <img src="./images/paypal.png" className="paypal" alt="Paypal" />
                <a className="minify" onClick={this.expand} alt="My cart"></a>
                <span className={this.state.prods.length ? 'pulse' : 'hidden'}>{this.state.prods.length}</span>
            </div>
        )
  }
}
    
export default withCookies(Cart)

The Product component:

import React from 'react';

class Product extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            showDetails: false,
            showModal: false,
            cart: []
        }
        this.imgPath = './images/catalog/'
    }
    render() {
    return (
        <div className="Product">
            <section>
                <img src={this.imgPath + this.props.product.image} />
            </section>
            <section>
                <div>
                    <h2>{this.props.product.title}</h2>
                    <h3>{this.props.product.artist}</h3>
                    <p>Product: {this.props.product.product_type}</p>
                    <h4>&#36;{this.props.product.price}</h4>
                    <button className="button" 
                    id={this.props.product.id} onClick={this.props.handler}>Add to cart</button>
                </div>
            </section>
        </div>
        )
    }
}

export default Product

If I log this.props.handler I get undefined. Everything works apart from the click handler, I was wondering if it might have something to with the async function. I am very new to React, there are still some concepts I'm not sure about, so any help is appreciated.

Upvotes: 1

Views: 3707

Answers (1)

Benjamin
Benjamin

Reputation: 3656

Okay, I see a few issues here.

First, there is no need to call this.handleClick = this.handleClick.bind(this) in the constructor, because you are using an arrow function. Arrow functions do not have a this context, and instead, accessing this inside your function will use the parent this found in the Class.

Secondly, it is wrong to store components in state. Instead, map your importedProducts inside the render function.

Thirdly, the issue with your handler is that this.props.handler doesn't actually call handleClick. You will notice in the definition handler={(product) => this.handleClick} it is returning the function to the caller, but not actually calling the function.

Try this instead.

class Product extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <button className="button" id={this.props.product.id} onClick={this.props.handler}>
          Add to cart
        </button>
      </div>
    );
  }
}

export default Product;
import Product from './Product'

class Cart extends React.Component {
  constructor(props) {
    super(props);
  }

  handleClick = (o) => {
    console.log('click');
  };

  render() {
    return (
      <div>
        {importedProducts.map((product) => {
          return <Product key={product.id} product={product} handler={() => this.handleClick(product)} />;
        })}
      </div>
    );
  }
}

export default Cart;

Upvotes: 3

Related Questions