jbetancur / react-data-table-component

A responsive table library with built-in sorting, pagination, selection, expandable rows, and customizable styling.
https://react-data-table-component.netlify.app
Apache License 2.0
2.04k stars 412 forks source link

DataTable is not expanding the row on row object property change #511

Closed sk364 closed 4 years ago

sk364 commented 4 years ago

Issue Check list

Describe the bug

DataTable is not expanding the row on row object property change

To Reproduce

Steps to reproduce the behavior:

  1. Use expandableRowExpanded method prop on DataTable: row => row.expandMe
  2. Set row.expandMe to false on initial load
  3. Update row.expandMe to true through button click

Expected behavior

Should expand the row.

Versions (please complete the following information)

Additional context

Expands the row when you change the page and come back.

How do I force re-render the table row?

jbetancur commented 4 years ago

Hard to tell without any code or a sandbox, but if I had to guess you need to make sure your data is being updated via React State and that DataTable is using that state.

sk364 commented 4 years ago
// TitleCell.jsx

import React from "react";
import { connect } from "react-redux";

const TitleCell = props => {
    const { row, onFetchSubObjects, onUnexpandRow } = props;
    return(<div onClick={() => {
        if (!row.isExpanded) onFetchSubObjects(row);
        else onUnexpandRow(row.id);
    }}>
        { /*  some html  */ }
    </div>);
};

export default connect(undefined, dispatch => {
    return {
        onFetchSubObjects: row => {
            // dispatches redux event to fetch sub objects which is picked by a saga
            //  which after successful fetch will dispatch an event with the fetched
            // objects, which  is then picked by the reducer to update the table data
        },
        onUnexpandRow: rowId => {
            // dispatches redux event to set `isExpanded` to false
        }
    }
})(TitleCell);
// Table.jsx

import React from "react";
import DataTable from "react-data-table-component";
import { connect } from "react-redux";
import TitleCell from "./TitleCell";
import SubTable from "./SubTable";

const columns = [
{
    id: "title",
    name: "Title",
    sortable: true,
    right: true,
    cell: row => <TitleCell row={row} />
},
{
    // ... more columns
}
];

const Table = props => {
    const { data, onFetchSubObjects } = props;
    const handleRowExpansion = (isExpanded, row) => {
        if (isExpanded) onFetchObjects(/* params */);
    };

    return(
        <DataTable
            data={data}
            columns={columns}
            expandableRows
            expandableRowsComponent={<SubTable columns={columns} />}
            onRowExpandToggled={handleRowExpansion}
            expandOnRowClicked={false}
            expandableRowExpanded={row => row.isExpanded}
    );
};

export default connect(
    state => {
        return {
            data: getTableData(state),
        };
    },
    dispatch => {
        onFetchObjects: () => {}
    }
)(Table);
// tableReducer.jsx

/*
    Initial items in data:
        [{
            id: 1,
            name: "name",
            children: []
        }, {
            ...
        }]

    `isExpanded` exists only when the sub objects are getting received
*/

export const tableReducer = (state = [], action) => {
    switch(action.type) {
        case "RCV_OBJECTS":
            return action.data;
        case "RCV_SUB_OBJECTS": {
            const { rowId, subObjects } = action;
            const rowIdx = state.findIndex(item => item.id === rowId);
            const row = state[rowIdx];
            return [
                ...state.slice(0, rowIdx),
                { ...row, children: subObjects, isExpanded: true },
                ...state.slice(rowIdx + 1)
            ];
        }
        case "UNEXPAND_ROW": {
            const { rowId } = action;
            const rowIdx = state.findIndex(item => item.id === rowId);
            const row = state[rowIdx];
            return [
                ...state.slice(0, rowIdx),
                { ...row, isExpanded: false },
                ...state.slice(rowIdx + 1)
            ];
        }
    }
};

Codesandbox is throwing a SSL error, so can't seem to upload there. Let me know if you need more context.

gnomewoood commented 4 years ago

I have the same issue. Please use the code sandobox. Just use a context menu to select rows and click on the button.

If I go to the new page then rows will be expanded.

jbetancur commented 4 years ago

Thank you @gnomewoood @sk364 for providing additional details.

This is not necessarily a bug but "was" an intended design. Historically, expandableRowExpanded was named defaultExpandedRows and intended to just set default expanded rows on initial render, but I think the name expandableRowExpanded has evolved to read otherwise and I may have meant to make it work in the way you are attempting. I'll look at prioritizing this feature/fix soon after I do some testing. Unfortunately, there is no workaround until then :(

Additional Details

Internally TableRow (where row expanded state is managed) uses const [expanded, setExpanded] = useState(defaultExpanded); to set the initial state:

https://github.com/jbetancur/react-data-table-component/blob/a10740a11d1a56704aab135e1772056eeaba89d8/src/DataTable/TableRow.js#L64

defaultExpanded is derived from expandableRowExpanded The issue with this is the expanded row state can only ever be set at initial render aka defaultExpanded. I believe a useEffect could be used here to make this work.

jbetancur commented 4 years ago

Ok, should be fixed in 6.4.0 with some caveats:

This is a one-way deal. While this allows you to change the state of the expanded rows and handle cases like expand all/collapse all it will only change the default expanded state. Clicking an individual expander on a row will not change expandableRowExpanded and the expanded state on your data. You can mess around with onRowExpandToggled and see if you can reconcile the state.

I realize this may be cumbersome, but the expand row feature was never intended to get into these edge cases. Simply, just allow you to have expandable rows. So, to get all these edge cases to work will require some significant refactor internally of how the expander state is handled. Probably similar to how selectableRows is handled.

It would be good to understand all the cases and the rationale as to why we need programmatic row expansions. I'm aware that we need a expand/collapse all feature, and aside from default expanded rows (when there is a rendering change) I would weigh if this makes the UX better or worse to allow rows to be toggled all over the place.