0

so I've got a table displaying multiple jobs from an API call. Each table row has a button that opens a bootstrap modal displaying further information about said job.

My problem is that the modal doesn't seem to be opening for the row clicked. It always opens a specific row (the one with the lowest id). Anyone know what I'm doing wrong? ModalComponent

render() {
  console.log("job: ", this.props.id)
  return (
    <div className="container">
      <ReactBootstrap.Modal
        {...this.props}
        size="lg"
        aria-labelledby="contained-modal-title-vcenter"
        centered
      >
        <ReactBootstrap.Modal.Header closeButton>
          <ReactBootstrap.Modal.Title id="contained-modal-title-vcenter">
            {this.props.title}
            {this.props.employer}
          </ReactBootstrap.Modal.Title>
        </ReactBootstrap.Modal.Header>
        <ReactBootstrap.Modal.Body>
          <h4>Would you like to apply to this job?</h4>
          <p>{this.props.description}</p>
        </ReactBootstrap.Modal.Body>
        <ReactBootstrap.Modal.Footer>
          <button className="btn btn-primary" onClick={this.applyJob}>Apply</button>
          <button className="btn btn-warning" onClick={this.props.onHide}>Close</button>
        </ReactBootstrap.Modal.Footer>
      </ReactBootstrap.Modal>
    </div>
  )
}

The console.log spits out the id of every row in the table.

My TableComponent passes the job details of each job into the ModalComponent like so: TableComponent

<tbody>
  {
    this.props.response.map(    //  map allows you to loop around items
      job =>  //  a key is used to identify a row
        <tr key={job.id}>
          <td>{job.employer}</td>
          <td>{job.jobTitle}</td>
          <td>{job.county}</td>
          {/* <td>{job.applied + ''}</td> */}
          <td>
            {this.checkApplied(job.applied)}
          </td>
          <td><button className="btn btn-info" onClick={() => this.setState({ addModalShow: true })}>Info</button></td>
          <ApplyComponent
            show={this.state.addModalShow}
            onHide={addModalClose}
            id={job.id}
            title={job.jobTitle}
            employer={job.employer}
            county={job.county}
            description={job.description}
            status={job.applied}
          />
        </tr>
    )
  }
</tbody>

Here are images of the table and the modal: enter image description here

enter image description here

As you can see, the details form the bottom row is being displayed in the modal even when I click the top row.

Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
Neil
  • 95
  • 1
  • 16
  • Add a console.log or something in each modal that logs something unique (like and id), there's a really good chance its opening all of them, and you're only seeing the last one opened. You'll be able to see for sure by checking your console logs for how many ids were printed. – Brian Thompson Apr 27 '20 at 13:32
  • Does this answer your question? [React modal always take the last element of map function](https://stackoverflow.com/questions/59581998/react-modal-always-take-the-last-element-of-map-function) – Brian Thompson Apr 27 '20 at 13:35
  • It looks as though you are both right, however I'm struggling to implement the fix. – Neil Apr 27 '20 at 13:47
  • @BrianThompson I feel as if I'm doing the same as the solution in the linked post no? – Neil Apr 27 '20 at 13:58
  • `addModalShow` is a single property in state. When you click a item to open, the `onClick` sets that property to true. Every single item that is returned from the map is listening to that one value (`addModalShow`) to determine if it should be open. So no matter which one you click, they will all open. The misleading part that I pointed out, is that you only see the last one, not all of them. You have a few options on how to avoid this. I'll go ahead and add an answer with those options, but the problem is essentially the same as the linked question. – Brian Thompson Apr 27 '20 at 14:09
  • Agh, I understand you now. So I need to pass a unique value to the addModalShow? – Neil Apr 27 '20 at 14:16

2 Answers2

2

The Problem

You are mapping over several items that can each have their own modal. Each of those modals determines whether it is open or closed by the show prop.

The show prop is set to this.state.addModalShow for every mapped element.

The Result

Every modal will open and close at the same time since they are based on the same state value. Clicking any item will cause all to open or close.

The visible result is that you will always see the last modal as the open modal, since it was the last to render and will be on top.

The Solution

Here is one solution that would require minimal changes:

Use a different value in state for each item.

this.props.response.map(
  job =>
    <tr key={job.id}>
      <td>{job.employer}</td>
      <td>{job.jobTitle}</td>
      <td>{job.county}</td>
      <td>
        {this.checkApplied(job.applied)}
      </td>
      <td>
      <button 
        className="btn btn-info" 
        onClick={() => {
          // Use a computed property name to make it unique based on the ID
          this.setState({ ['show_'+job.id]: true })
        }
      >Info</button></td>
      <ApplyComponent
        show={this.state['show_'+job.id]} // Use the same state value as the prop
        onHide={() => {
          // Follow the same process for closing
          this.setState({ ['show_'+job.id]: false})
        }}
        id={job.id}
        title={job.jobTitle}
        employer={job.employer}
        county={job.county}
        description={job.description}
        status={job.applied}
      />
    </tr>
)
Community
  • 1
  • 1
Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
0

simple solution is that use selectedRow or selectedJob or selectedCompany
when use click on specific button which exist in each row

it will dynamically show the information for selected row Model dialog

    function handleClickCompanyCard(company: Company) {
            setSelectedCompany(company);
            setShowState(true);
          }
return (
     <Fragment>
    {companiesData.map((company: Company, id) => (
<div
  onClick={() => handleClickCompanyCard(company)}
>
  row for one company
</div>
    )}
    </Fragment>


    <Fragment>
      <Modal show={showState} onHide={handleClose}>
        <Modal.Header closeButton>
          <Modal.Title>{selectedCompany?.company_name}</Modal.Title>
        </Modal.Header>
        <Modal.Body>Woohoo, youre reading this text in a modal!</Modal.Body>
        <Modal.Footer>
          <Button variant="secondary" onClick={handleClose}>
            Close
          </Button>
          <Button variant="primary" onClick={handleClose}>
            Save Changes
          </Button>
        </Modal.Footer>
      </Modal>
    </Fragment>
);
Hassan Saeed
  • 6,326
  • 1
  • 39
  • 37