Search code examples
reactjsreact-bootstrap-tablereact-modal

React Bootstrap Table has button in each row to create modal, but one button will call modal's render multiple times


I have a React Bootstrap Table, each page of the table has 20 records. In each row, I added a button using following line

function attachFormatter(cell, row){
     return (
        <AttachmentManager />
     );
 }
 <TableHeaderColumn key={uuid.v4()}
                   dataField={column.dataField}
                   dataFormat={attachFormatter}
                   hidden={hide}>
                    {column.label}
</TableHeaderColumn>

Then I have 20 buttons in this page and each row has one button. I intended to open a modal if click one of the button. But when I click one button, the openModal() runs one time as expected, but the render() function of my AttachmentManager will run 20 times. How to solve this?

export default class AttachmentManager extends React.Component {
    constructor (props) {
        super(props);
        this.state = {showModal: false};
    }

    openModal() {
        alert('test');
    }

    render () {
        return (
      <button onClick={this.openModal.bind(this)} className="btn btn-default">Add Projects
        <AttachmentModal show={this.state.showModal}/>
      </button>
        );
    }
}

And following is my modal

import React from 'react';
import SimpleModal from '../common/SimpleModal';
export default class AttachmentModal extends React.Component {

    constructor (props) {
        super(props);
    }

    render () {
        return (
            <SimpleModal showModal={this.props.show}
                       onToggleModal={this.props.onHide}
                       title={this.props.title}
                       onCancelClick={this.props.onHide}
                       onPrimaryButtonClick={this.props.onPrimaryButtonClick}
                       cancelText="Cancel"
                       primaryButtonText={this.props.primaryButtonText}
                       loading={this.props.loading}
                       backdrop='static'
                       bsStyle={this.props.bsStyle}>
                {this.props.children}
            </SimpleModal>
        );
    }
}

Solution

  • I was also facing the similar problem and solved it like this.

    In attachFormatter, pass the row value as props

    function attachFormatter(cell, row){
         return (
            <AttachmentManager row={row} />
         );
     }
     <TableHeaderColumn key={uuid.v4()}
                       dataField={column.dataField}
                       dataFormat={attachFormatter}
                       hidden={hide}>
                        {column.label}
    </TableHeaderColumn>
    

    In AttachmentManager, On click of button the set selectedRow and showModal value. You can use isObjectEquivalent function to compare the row props and selectedRow value.

    export default class AttachmentManager extends React.Component {
      constructor(props) {
       super(props);
       this.state = {
        showModal: false,
        selectedRow: null
       };
      }
    
      openModal() {
       this.setState((prevState, props) => {
        return {
         selectedRow: props.row,
         showModal: true
        }
       });
      }
    
      isObjectEquivalent(a, b) {
       // Create arrays of property names
       var aProps = Object.getOwnPropertyNames(a);
       var bProps = Object.getOwnPropertyNames(b);
    
       // If number of properties is different,
       // objects are not equivalent
       if (aProps.length != bProps.length) {
        return false;
       }
    
       for (var i = 0; i < aProps.length; i++) {
        var propName = aProps[i];
    
        // If values of same property are not equal,
        // objects are not equivalent
        if (a[propName] !== b[propName]) {
         return false;
        }
       }
    
       // If we made it this far, objects
       // are considered equivalent
       return true;
      }
    
      render() {
        return ( 
        <div>
            <button onClick = {this.openModal.bind(this)} className = "btn btn-default"> Add Projects </button> 
            {this.state.showModal && this.isObjectEquivalent(this.props.row, this.state.selectedRow) ? ( < AttachmentModal show = {this.state.showModal} />) : null} 
         </div>
          );
         }
        }
    

    Hope this help you.