React: Getting props from multiple arrays using .map

Viewed 171

I'm building an ERP-style app using MERN and Redux. My backend is working well, I can add things through my API and all, but I'm having some stupid issues with my front-end.

The idea is that I have item-objects and want to access them through a modal. The modal itself works, and the button used to open the modal actually gets the names of the objects through the loop.

The issue comes in inside the ModalBody - it only fetches the information from the first object in the table. I've tried methods such as this.props.customer, items.customer, this.items.customer and many, many more without success.

It would be cool if I can export the component as a class too, but if it's impossible, that's okay I guess.

Do you guys have any idea on how I can make it work?

render(){
  const { items } = this.props.item;

  return (
    <Container>
      <ListGroup>
        <TransitionGroup className="shopping-list">
          {items.map(({ _id, name, customer, location, dilemma }) =>
            <CSSTransition key={_id} timeout={500} classNames="fade">
              <ListGroupItem key={_id}>
                <Button color="light" style={{ width: "100%" }} onClick={this.toggle}>{name}</Button>
                <Modal isOpen={this.state.modal} toggle={this.toggle}>
                  <ModalHeader toggle={this.toggle} key={_id}>{name}</ModalHeader>
                  <ModalBody key={index}>
                    <p>Customer: {customer}</p>
                    <p>Location: {location}</p>
                    <p>Dilemma: {dilemma}</p>
                  </ModalBody>
                </Modal>
              </ListGroupItem>
            </CSSTransition>
          )}
        </TransitionGroup>
      </ListGroup>
    </Container>
  );
}
3 Answers

You don't need to generate a modal for every item. Just list items and create one modal. After that, when an item clicked pass the related item to modal and set visible.

I create a mini sample for you.

import React, { Component } from 'react';
import { render } from 'react-dom';

const Modal = (props) => {
  return (
    props.visible ? (
      <div>
        Product info
        <hr />
        <h3>{props.item.name}</h3>
      </div>
    ) : ""
  )
}

class App extends Component {
  constructor() {
    super();
    this.state = {
      selectedItem: null,
      modalState: false
    };
  }

  data = [{
    id: 1,
    name: "p1"
  }, {
    id: 2,
    name: "p2"
  }, {
    id: 3,
    name: "p3"
  },
  ]

  handleItemClick = (id) => {
    this.setState({
      modalState: true,
      selectedItem: this.data.find(a => a.id === id)
    })
  }

  render() {
    const { modalState, selectedItem } = this.state

    const mappedList = this.data.map(
      (i) => (
        <li key={i.id} onClick={() => this.handleItemClick(i.id)}>{i.name}</li>
      )
    );

    return (
      <div>
        <ul>{mappedList}</ul>
        <Modal visible={modalState} item={selectedItem} />
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

https://stackblitz.com/edit/react-gheoek

I am not sure I get what is your intention, but I would pass the items array to the Modal component as prop, then use map in the Modal component AFTER the ModalBody. The way you are doing now you are rendering a lot of stuff (including Modal) for every item in the itemsarray.

I'm not sure how your array looks like but have you tried using the map() like this?

{
  items.map((item, key) =>
    <CSSTransition key={key} timeout={500} classNames="fade">
      <ListGroupItem key={item._id}>
        <Button color="light" style={{ width: "100%" }} onClick={this.toggle}>{item.name}</Button>
        <Modal isOpen={this.state.modal} toggle={this.toggle}>
          <ModalHeader toggle={this.toggle} key={item._id}>{item.name}</ModalHeader>
          <ModalBody key={key}>
            <p>Customer: {item.customer}</p>
            <p>Location: {item.location}</p>
            <p>Dilemma: {item.dilemma}</p>
          </ModalBody>
        </Modal>
      </ListGroupItem>
    </CSSTransition>
  )
}

Also are you looking to render all items from the array in one specific modal or would you like to fill up one modal with an item from the array? In the last case it would be better to render just one modal and pass the item props for that specific item to the modal.

Related