gregnb / mui-datatables

Datatables for React using Material-UI
MIT License
2.71k stars 932 forks source link

Non-serverSide filtering/searching re-renders whole MUIDatatable #739

Open dclorena11 opened 5 years ago

dclorena11 commented 5 years ago

Expected Behavior

When we upgraded our version from 2.0.0-beta.58 to 2.5.1 we found that changing state in the component re-rendered the whole table, but in the previous version it only seemed to re-render the TableBody. When we filter with serverSide: false we would like for only the TableBody to re-render and include filtered changes.

Note: We also have are using onTableChange and a customFooter in our options.

Current Behavior

Currently when we filter, whole table re-renders, check box column shows up on the left side of table, chips don't show on the top header, our customFooter doesn't not render correctly with totals, records don't filter.

Steps to Reproduce (for bugs)

You'll see that a lot of complexity below is us trying to calculate the Quantity Submitted based on the filtered results we get onTableChange. We were so excited to have this work in a previous version, so sad that it's no longer supported. It seems like 2.5.1 assumes you're using onTableChange for serverSide filtering. We tried using useRef hooks to stop rendering without success. My attempt at reproducing in sandbox but copy pasting below as well..

export const splitDateTime = results => {
  return results.map(currentValue => {
    return currentValue['Timestamp'] !== null && typeof currentValue['Timestamp'] !== 'undefined'
      ? Object.assign({}, currentValue, {
          Time: `${currentValue['Timestamp'].split(' ')[1]} ${
            currentValue['Timestamp'].split(' ')[2]
          }`,
          Timestamp: currentValue['Timestamp'].split(' ')[0],
        })
      : currentValue;
  });
};

export const unfilteredQuantitySubmitted = results => {
  if (results && results.length > 0) {
    let quantitySubmitted = results.map(record => stringToNumber(record['Quantity Submitted']));

    return quantitySubmitted.reduce((a, b) => a + b, 0);
  }
};

export const filteredQuantitySubmitted = state => {
  let columnNames = state.columns.map(column => column.name);
  let index = columnNames.indexOf('Quantity Submitted');
  let quantitySubmitted = state.displayData.map(record =>
    stringToNumber(record.data[index].props.value)
  );
  return quantitySubmitted.reduce((a, b) => a + b, 0);
};

const ProductionSubmissionsTable = props => {
  const { fetching, results } = props;

  const [quantitySubmitted, setQuantitySubmitted] = useState(0);

  useEffect(
    () => {
      setQuantitySubmitted(unfilteredQuantitySubmitted(results));
    },
    [results, setQuantitySubmitted]
  );

  if (fetching) {
    return <Loading type="linear" />;
  }

  if (results && results.length > 0) {
    const columns = columnOptions;
    const options = {
      filterType: 'dropdown',
      responsive: 'scroll',
      selectableRows: false,
      print: false,
      download: false,
      rowsPerPage: 100,
      serverSide: false,
      onTableChange: (action, state) => setQuantitySubmitted(filteredQuantitySubmitted(state)),
      customFooter: function footer(count, page, rowsPerPage, changeRowsPerPage, changePage) {
        return (
          <CustomFooter
            page={page}
            rowsPerPage={rowsPerPage}
            count={count}
            quantitySubmitted={quantitySubmitted}
            onChangePage={(_, newPage) => changePage(newPage)}
            onChangeRowsPerPage={event => changeRowsPerPage(event.target.value)}
            rowsPerPageOptions={[10, 15, 100]}
          />
        );
      },
    };

    return (
      <MuiThemeProvider theme={theme}>
        <MUIDataTable
          title="Production Submissions"
          data={splitDateTime(results)}
          columns={columns}
          options={options}
        />
      </MuiThemeProvider>
    );
  } else {
    return <div>Unable to render Ingredient Submissions Table</div>;
  }
};

and customfooter

const CustomFooter = ({
  page,
  rowsPerPage,
  count,
  quantitySubmitted,
  onChangePage,
  onChangeRowsPerPage,
  rowsPerPageOptions,
}) => {
  return (
    <MuiTableFooter>
      <MuiTableRow>
        <MuiTableCell>Quantity Submitted: {formatNumber(quantitySubmitted)}</MuiTableCell>
        <MuiTablePagination
          page={page}
          rowsPerPage={rowsPerPage}
          count={count}
          onChangePage={onChangePage}
          onChangeRowsPerPage={onChangeRowsPerPage}
          rowsPerPageOptions={rowsPerPageOptions}
        />
      </MuiTableRow>
    </MuiTableFooter>
  );
};

Your Environment

Tech Version
Material-UI ^3.9.2
MUI-datatables ^2.5.1
React ^16.8.6
browser chrome
etc using onTableChange, serverSide:false, customFooter in table options
gabrielliwerant commented 5 years ago

There's a lot going on here. Can you pare this down to a single specific issue? Also, I could not load the codesandbox example, as it appears to error-out immediately.

There's a lot that has changed between the beta and the most recent version. It's likely that you will need to modify your code if you want to take advantage of the latest versions.

I'm fairly certain onTableChange will still work for you, but you would likely have to modify it to maintain more of your state, as this is the direction the table is moving. We can't rely on internal derived state, unfortunately, as this means that no one can alter the data going in as props and benefit from react bindings. Additionally, you may want to reread the documentation, as there are a lot of features you might not be taking advantage of. For example, if you just need to make updates on filter change, then you've got a callback just for that: onFilterChange. No need to use the generic onTableChange in that case.