Search code examples
react-table

React table maximum update depth exceeded


I'm trying to render a table of data fetch from an API.

The first render is fine, everything seems to be ok, but when i'm trying to use SortBy or when i'm trying to add an input that i could use to call filtered data to the API i got the error :

react-dom.development.js:27292 Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    at checkForNestedUpdates (react-dom.development.js:27292:1)
    at scheduleUpdateOnFiber (react-dom.development.js:25475:1)
    at dispatchReducerAction (react-dom.development.js:17452:1)
    at react-table.development.js:944:1
    at react-table.development.js:253:1
    at commitHookEffectListMount (react-dom.development.js:23150:1)
    at commitLayoutEffectOnFiber (react-dom.development.js:23268:1)
    at commitLayoutMountEffects_complete (react-dom.development.js:24688:1)
    at commitLayoutEffects_begin (react-dom.development.js:24674:1)
    at commitLayoutEffects (react-dom.development.js:24612:1)
const TableAnime = () => {

  const [animes, setAnimes] = useState({});
  const [loadingData, setLoadingData] = useState(true);
  const [endpoint, setEndpoint] = useState('/anime?page[limit]=10&page[offset]=0')

  const getAnimes = async () => {
    const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
    setAnimes(response.data);
    setLoadingData(false);
  }

  useEffect(() => {
    getAnimes()
  }, []);

  const handleForm = async () => {

    const results = await axios.get(`https://kitsu.io/api/edge/anime`);
    setAnimes(results.data)
    console.log(animes)
  }
    
  const data = useMemo(() => (animes.data), [animes.data]);

  
  return (
    <div>
      <Form handleForm={handleForm}/>
      <h1>Catalogue</h1>
      {loadingData ? 
        (
          <p>Loading Please wait...</p>
        ) : (
        
          <Table animes={animes.data} />        
        )
      }
    </div>
  );
}

// == Export
export default TableAnime;

Table.js :

// == Import
import { useTable } from "react-table";
import { useMemo } from "react";
import { Link } from 'react-router-dom';
import moment from "moment"

import RowItem from "./RowItem";
import HeaderItem from "./HeaderItem";


// == Composant
const Table = ({animes}) => {
  
  const columns = useMemo(() => [ 
    { Header: "Titre", accessor: "attributes.canonicalTitle"},
    { Header: "Titre Japonais", accessor: "attributes.titles.ja_jp"},
    { Header: "Age recommandé", accessor: "attributes.ageRatingGuide"},
    { Header: "Date de sortie", accessor: d => moment(d.attributes.startDate).format("DD/MM/YYYY")},
    { Header: "Rang", accessor: "attributes.popularityRank"},
    { Header: " ", accessor: d => <Link to={`/anime/${d.id}`}>Voir les détails</Link>},
  ]);

  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
  } = useTable({columns, data: animes})

  return (
    <>
      <table {...getTableProps()}>
        <thead>
        {headerGroups.map(headerGroup => (
            <HeaderItem headerGroup={headerGroup} key={Date.now()}/>
        ))}
        </thead>
        <tbody {...getTableBodyProps()}>
        {rows.map(row => {
          prepareRow(row)
          return (
            <RowItem key={(Date.now()*Math.random())} row={row} />
          )
        })}
        </tbody>
      </table> 
    </>
  );
}

// == Export
export default Table;

Then i got RowItem and HeaderItem :

const RowItem = ({row}) => {
  return (
    <tr {...row.getRowProps()}>
      {row.cells.map(cell => {
        return (
          <td {...cell.getCellProps() }>
            {cell.render('Cell')}
          </td>
        )
      })}
    </tr>
  );
}

export default RowItem
const HeaderItem = ({headerGroup}) => (
  <tr {...headerGroup.getHeaderGroupProps()}>
    {headerGroup.headers.map(column => (
      <th {...column.getHeaderProps()} >
        {column.render('Header')}
      </th>
    ))}
  </tr>
);

export default HeaderItem ;

From what i understand that could be because i didn't use useMemo properly but i don't know how to do that. I tried at multiple places but nothing worked. I'm using React Table for the first time and i'm new to React, so I'm sorry if the answer seems really easy but i really coudn't figured it out myself.


Solution

  • You need to cache the getAnime fetcher function by using useCallback and pass the endpoint as the dependency to make sure the fetcher fn is only called once (twice in strict mode).

    So, change this:

      const getAnimes = async () => {
        const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
        setAnimes(response.data);
        setLoadingData(false);
      }
    
      useEffect(() => {
        getAnimes()
      }, []);
    

    into this:

      const getAnimes = useCallback(async () => {
        const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
        setAnimes(response.data);
        setLoadingData(false);
      }, [endpoint]);
    
      useEffect(() => {
        getAnimes();
      }, [getAnimes]);
    

    You can check here