Mui-Datatables tableState in function causes infinite loop

Viewed 1541

New to React... I am trying to set and save the Mui-Datatable tableState in a React Function. When I use the following code it I get the following error:

Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate.

How can I fix my code so this doesn't happen? I converted it from a Class to a Function so I could use a hook.

import React, {useEffect, useState} from 'react';
import MUIDataTable from "mui-datatables";
import { MuiThemeProvider } from '@material-ui/core/styles';
import { Typography, Box } from '@material-ui/core';
import MuiDataTableCSS from '../../Styles/MuiDataTable';
import Button from '@material-ui/core/Button';

export default function ProposalTable() {
const [isLoading, setIsLoading] = useState(false);
const [cols, setCols] = useState([]);
const [users, setUsers] = useState([]);
const [currTable, setCurrTable] = useState({});

const options = {
    filterType: 'multiselect',
    responsive: 'standard',
    fixedHeader: true,
    elevation: 1,
    selectableRows: false,
    tableBodyHeight: 'calc(100vh - 200px)',
    print: false,
    resizableColumns: false,
    draggableColumns: {
      enabled: true,
    },
    onTableInit: handleTableInit, 
    onTableChange: handleTableChange,
};

function handleTableInit(action, tableState){
    console.log('handleTableInit: ', tableState);
    setCurrTable({ tableState });
}; 
function handleTableChange(action, tableState){
    setCurrTable({ tableState });
    console.log('handleTableChange: ', tableState);
};

useEffect(() => {
    let cols = [];
    setIsLoading(true); // start the loader
    fetch("/data.json")
    .then(response => response.json())
    .then(data => {
        setIsLoading(false); // stop the loader
        for(var key in data[0]){
            var newKey = key.replace(/_/g, " ");
            cols = [...cols, { name: newKey, label: key}]
        }
        setUsers(data);
        setCols(cols);
    })
    .catch(error => {
        console.log(error);
    })
  .finally(() => {
      setIsLoading(false); // stop the loader
  });
  }, []);

if (isLoading) {
    return <p>Loading...</p>;
}

return (
    <div id="muiDataTable">
        <Typography component="h1" variant="h4" align="center">
            <Box pb="1em">
                Proposals
            </Box>
            <Button style={{ textTransform: 'none' }} onClick={() => handleTableChange('button', currTable)}>Save Table</Button>
        </Typography>
        <MuiThemeProvider theme={MuiDataTableCSS}>
            <MUIDataTable
                data={users}
                columns={cols}
                options={options}/>
        </MuiThemeProvider>
    </div>
)
}

Mui-Datatable Example that uses Class: https://codesandbox.io/s/github/gregnb/mui-datatables?file=/examples/on-table-init/index.js

Mui-Datatable Example Code:

import React from 'react';
import ReactDOM from 'react-dom';
import MUIDataTable from '../../src';

class Example extends React.Component {
constructor(props) {
super(props);

this.state = {
  table: {}
};
this.handleTableInit = this.handleTableInit.bind(this);
this.handleTableChange = this.handleTableChange.bind(this);
}

/** onTableInit gives access to initial MuiDataTable state
   *  if the application needs access to internal state prior to
   *  the user performing a table mutation (sort, filter, etc.)
   *  that triggers onTableChange
 */
handleTableInit = (action, tableState) => {
console.log('handleTableInit: ', tableState);
this.setState({ table: tableState });
};

handleTableChange = (action, tableState) => {
  console.log('handleTableChange: ', tableState);
  this.setState({ table: tableState });
};

columns = ['Name', 'Title', 'Location', 'Age', 'Salary'];

data = [
  ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, 400000],
  ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, 110000],
  ['Danny Leon', 'Computer Scientist', 'Newark', 60, 220000],
  ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, 180000],
  ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, 99000],
  ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, 90000],
  ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, 140000],
  ['Justice Mccarthy', 'Attorney', 'Tucson', 26, 330000],
  ['Silver Carey', 'Computer Scientist', 'Memphis', 47, 250000],
  ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, 190000],
  ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, 80000],
  ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, 45000],
  ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, 142000],
];

options = {
  filter: true,
  selectableRows: 'multiple',
  filterType: 'dropdown',
  responsive: 'standard',
  rowsPerPage: 10,
  download: false, // hide csv download option
  onTableInit: this.handleTableInit,
  onTableChange: this.handleTableChange,
};

render() {
  return <MUIDataTable title={'ACME Employee list'} data={this.data} columns={this.columns} options={this.options} />;
  }
}

export default Example;
2 Answers

You can specify the change action you want to update the table state based on it to avoid the infinite loop

    onTableChange: (action, tableState) => {
            switch (action) {
                case 'changePage':
                    setCurrTable({ tableState });
                    break;
                case 'sort':
                    setCurrTable({ tableState });
                    break;
                default:
                    console.log('action not handled.');
            }
       }

I ran in to a similar problem trying to set some kind of state from the callback. It turns out that "onTableChange" fires whenever any kind of state changes in the parent component even if its irrelevant to the table. Which leads to the problem where setting state in the onTableChange callback can cause an infinite loop. I wrapped the table component in a class component and used "shouldComponentUpdate" as a work around I've filed an issue on github here https://github.com/gregnb/mui-datatables/issues/1738

Related