1

In an instance of react-table, I am adding a button to each row to launch a react-modal. This works as expected on the first page of the table, but on subsequent pages the incorrect id prop is passed into my custom ReactModal's listId. The id that is passed seems to be the one of the row in the same position on the first page of the table. So, the modal components in the first position of pages 2+ receive the id prop of the row in the first position on the first page.

I think the line in question is in Home below where Cell: (row: any) => <ListDetailsModal listId={row.value} />,. The value of {row.value} doesn't seem to work correctly on pages of the table that are not the first.

How can I ensure the correct id is passed into ListDetailsModal's listId on all pages of Home's ReactTable?

Links: Live Demo | Source

react-table component class:

export class Home extends React.Component<RouteComponentProps<{}>, IFilterListsState> {
    constructor(props: any) {
        super(props);
        this.state = { filterLists: [], loading: true };
        fetch("https://api.filterlists.com/v1/lists")
            .then(response => response.json() as Promise<IFilterListSummaryDto[]>)
            .then(data => { this.setState({ filterLists: data, loading: false }); });
    }

    render() {
        const contents = this.state.loading
            ? <p>
                  <em>Loading...</em>
              </p>
            : Home.renderFilterListsTable(this.state.filterLists);
        return <div>
                   {contents}
               </div>;
    }

    private static renderFilterListsTable(filterLists: IFilterListSummaryDto[]) {
        return <ReactTable
                   data={filterLists}
                   defaultSorted={[{id: "name"}]}
                   columns={[
                       {
                           Header: "Name",
                           accessor: "name",
                           filterable: true,
                           filterMethod: (filter: any, row: any) => row[filter.id].toUpperCase().includes(filter.value.toUpperCase()),
                           sortMethod: (a: any, b: any) =>  a.toUpperCase() > b.toUpperCase() ? 1 : -1
                       },
                       {
                           Header: "Details",
                           accessor: "id",
                           sortable: false,
                           Cell: (row: any) => <ListDetailsModal listId={row.value} />,
                           style: { textAlign: "center" },
                           width: 100
                       }
                   ]}/>;
    }
}

react-modal component class:

export default class ListDetailsModal extends React.Component<any, any> {
    private readonly listId: any;

    constructor(props: any) {
        super(props);
        this.state = { showModal: false, loading: true };
        this.listId = props.listId;
        this.handleOpenModal = this.handleOpenModal.bind(this);
        this.handleCloseModal = this.handleCloseModal.bind(this);
    }

    handleOpenModal() {
        this.setState({ showModal: true });
        fetch(`https://api.filterlists.com/v1/lists/${this.listId}`)
            .then(response => response.json() as Promise<IFilterListDetailsDto[]>)
            .then(data => { this.setState({ filterListDetails: data, loading: false }); });
    }

    handleCloseModal() {
        this.setState({ showModal: false });
    }

    render() {
        const contents = this.state.loading
            ? null
            : <ReactModal
                  isOpen={this.state.showModal}
                  onRequestClose={this.handleCloseModal}
                  shouldCloseOnOverlayClick={true}>
                  <FilterListDetails details={this.state.filterListDetails}/>
                  <button onClick={this.handleCloseModal} className="btn btn-danger btn-block push-to-bottom">Close</button>
              </ReactModal>;
        return (
            <div>
                <button onClick={this.handleOpenModal} className="btn btn-primary btn-block">Details</button>
                {contents}
            </div>
        );
    }
}
Collin Barrett
  • 2,441
  • 5
  • 32
  • 53
  • pointers from react-table chat I am trying: "I suspect you will need to implement `componentDIdReceiveProps` in your modal wrapper component. This is because the component is already mounted so the `constructor` is not going to be called again until it is destroyed then mounted again. or possibly `comonentDidMount` - that’s what I would be tinkering with and dropping in `console.log` statements to see what we being called when the page changes. the other thing you might want to consider is giving your component a `key` that you generate uniquely for each cell, that might force React to redraw" – Collin Barrett Feb 17 '18 at 22:59

1 Answers1

1

Ah, the problem was that the already rendered component was not getting updated with the new props when the user changed pages of the table. All that was needed was adding this to capture the new props.

componentWillReceiveProps(nextProps: any) {
    this.listId = nextProps.listId;
}
Collin Barrett
  • 2,441
  • 5
  • 32
  • 53