Ok then. I have this code and is just for practice so it could be a bit messy (please ignore the cammelCase convention or bad practices unless they are the cause of the problem, beacause I've already take note of it), howerver i'm going to describe what it does and I'll show the code.
I have this componet that is rendered without data at first time, then when the users click a button (in a parrent component) an API it's called (also in the parrent) and the response is sended to this problematic component through props. After de data from the API is recived (props.rows), a useEffect is executed to format this data and store it in state (Rows). When Rows value changes, another useEffect should be executed and manipulate Rows (without mutating it) to create another kind of data (TotalPages). The thing is that when I call setTotalPages() this produce an infinite loop and I get the error Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
I'll show the code now
This is the problematic component
import '../Css/RGrid.css';
//import {useLayoutEffect} from 'react';
import React, {useCallback, useState} from 'react';
import {useEffect} from 'react';
//import {useLayoutEffect} from 'react';
//import {useLayoutEffect} from 'react';
//import {Row} from 'react-bootstrap';
//import {act} from 'react-dom/cjs/react-dom-test-utils.production.min';
const RGridTest = props => {
const [Rows, setRows] = useState([]);
const [rowsPerPage, setRowsPerPage] = useState(10);
const [TotalRows, setTotalRows] = useState(0);
const [actualPageIndex, setActualPageIndex] = useState(0);
const [TotalPages, setTotalPages] = useState(0);
const [counter, setCounter] = useState(0);
const ddlPages_OnChange = value => {
try {
setRowsPerPage(value);
} catch (e) {
console.log(e.message);
}
};
const Export = value => {
console.log(value);
};
const EnabledPaging = () => {
let b = false;
try {
b = Rows.length > 0 && Rows.length < 9999 && rowsPerPage < 9999;
} catch (e) {
console.log(e);
}
return b;
};
const PrevPage = () => {
try {
if (actualPageIndex > 1) {
setActualPageIndex(actualPageIndex - 1);
}
} catch (e) {
console.log(e.message);
}
};
const NextPage = () => {
try {
if (actualPageIndex < TotalPages) {
setActualPageIndex(actualPageIndex + 1);
}
} catch (e) {
console.log(e.message);
}
};
const calcularPaginas = useCallback(() => {
if (Rows.length === 0) {
console.log('Sin filas');
return false;
}
console.log('llegaron las filas');
const cantidadFilas = Rows.length;
let iTotal = 0;
console.log(cantidadFilas);
if (cantidadFilas % rowsPerPage === 0) {
console.log('Division perfecta');
iTotal = Math.ceil(cantidadFilas / rowsPerPage);
}
console.log('Division imperfecta');
iTotal = Math.ceil(cantidadFilas / rowsPerPage);
console.log(cantidadFilas, rowsPerPage);
console.log(iTotal);
setTotalPages(iTotal);
}, [Rows]);
const ChangeId = () => {
let sText;
let oComplete;
try {
if (props.rows.length === 0) {
console.log('No change id');
return;
}
sText = JSON.stringify(props.rows);
sText = sText.replace('"' + props.ConfigurationId + '":', '"RowId":');
oComplete = JSON.parse(sText);
setRows(oComplete);
setTotalRows(oComplete.length);
console.log('ChangeId Rows');
console.log(oComplete.length);
} catch (e) {
console.log(e.message);
}
};
useEffect(() => {
ChangeId();
}, [props.rows]);
useEffect(() => {
calcularPaginas();
}, [Rows, calcularPaginas]);
return (
<div>
{props.isLoading ? (
<h2>Loading ...</h2>
) : (
<React.Fragment>
<span>
<select
className="Select"
name="ddlPages"
id="ddlPages"
key="ddlPages"
onChange={e => ddlPages_OnChange(e.target.value)}
>
<option value="10"> 10 </option>
<option value="25"> 25 </option>
<option value="50"> 50 </option>
<option value="100"> 100 </option>
<option value="9999"> All </option>
</select>
</span>
{props?.Export && (
<span>
<button value="csv" className="btn-2" onClick={e => Export(e.target.value)}>
CSV
</button>
<button value="xls" className="btn-2" onClick={e => Export(e.target.value)}>
Excel
</button>
<button value="pdf" className="btn-2" onClick={e => Export(e.target.value)}>
PDF
</button>
</span>
)}
<table width="99%" border="0" align="center">
<tr className="TrTittle">
<td className="TdTittle" align="center">
<a>{props.Tittle}</a>
</td>
</tr>
</table>
<table className="Table" key="tgrid" width="99%" align="center">
<thead key="thead">
<tr key="trHead">
{props.columns.map((column, idx) => {
return (
<th className="TableCellBold" width={column.WidthColumn}>
{column.Titulo}
</th>
);
})}
{props.ShowDelete && (
<th width="1%" className="TableCellBold">
Action
</th>
)}
</tr>
</thead>
<tbody>
{Rows.map((row, idx) => {
if (idx <= rowsPerPage) {
return (
<tr key={'tr' + idx}>
{props.columns.map((column, colx) => {
return (
<td className="TableCell" width={column.WidthColumn}>
{column.Selector(row)}
</td>
);
})}
{props.ShowDelete && (
<td className="TableCellBold" align="center">
<a href="#" onClick={() => props.DeleteId(row.RowId)}>
<img
className="imgDelete"
title="Next"
border="0"
width="18px"
height="18px"
></img>
</a>
</td>
)}
</tr>
);
}
})}
</tbody>
<tfoot>
<tr key="trFoot">
<td
key="tdFoot"
align="right"
colSpan={props.columns.length + 1}
className="TableCellBold"
>
{EnabledPaging() && (
<div className="DivFooter">
<a href="#" onClick={PrevPage()}>
<img
className="imgPrev"
title="Next"
border="0"
width="18px"
height="18px"
></img>
</a>
<a>
{' '}
Page {actualPageIndex + 1} / {TotalPages}{' '}
</a>
<a href="#" onClick={NextPage()}>
<img
className="imgNext"
title="Next"
border="0"
width="18px"
height="18px"
></img>
</a>
</div>
)}
</td>
</tr>
</tfoot>
</table>
</React.Fragment>
)}
<div>
<button onClick={() => setCounter(prev => prev + 1)}>Mostrar totalRows</button>
</div>
</div>
);
};
export default RGridTest;
This is the parrent, were the button that calls the API is pressed
import './Css/App.css';
import 'bootstrap/dist/css/bootstrap.css';
import GrillaCompleta from './Components/GrillaCompleta';
import {BrowserRouter as Router, Switch, Route, Link} from 'react-router-dom';
import RGrid from './Components/RGrid';
import RGridTest from './Components/RGridTest';
import {ListAll} from './Components/Helpers';
import React, {useState} from 'react';
import TestSocialReducer from './Components/TestSocialReducer';
function App() {
const [Dogs, setDogs] = useState([]);
const [isCargando, setIsCargando] = useState(false);
function FindDogs() {
setIsCargando(true);
ListAll().then(lDog => {
setDogs(lDog);
setIsCargando(false);
});
}
/*
useEffect(() => {
}, []);
*/
function Topics() {
return (
<div>
<h2>Topic</h2>
</div>
);
}
const GrillaConfiguracion = [
{
Titulo: 'Nombre',
Selector: fila => fila.name,
WidthColumn: '30%',
Ordenable: true,
},
{
Titulo: 'Tamaño',
Selector: fila => fila.breed_group,
WidthColumn: '30%',
},
];
return (
<Router>
<div>
<ul>
<li key="home">
<Link to="/">Home</Link>
</li>
<li key="GrillaCompleta">
<Link to="/GrillaCompletaPrueba"> Grilla Completa Dog Test </Link>
</li>
<li key="Topic">
<Link to="/topics">Topics</Link>
</li>
<li key="RGrilla">
<Link to="/RGrilla">RGrilla</Link>
</li>
<li key="GrillaTest">
<Link to="/RGrillaTest">RGrillaTest</Link>
</li>
<li key="Reducer Test">
<Link to="/TestSocialReducer">TestSocialReducer</Link>
</li>
</ul>
<Switch>
<Route path="/rGrilla">
<button key="btnFind" id="btnFind" onClick={FindDogs}>
Ver Perros
</button>
<br></br>
<RGrid
key="RGrid1"
Tittle="Grilla Dogs"
rows={Dogs}
columns={GrillaConfiguracion}
ShowDelete
Export
DeleteId={id => console.log(id)}
isLoading={isCargando}
/>
</Route>
<Route path="/rGrillaTest">
<button key="btnFindTest" id="btnFindTest" onClick={FindDogs}>
Ver Perros Test
</button>
<br></br>
<RGridTest
key="RGridTest"
Tittle="Grilla Dogs Test"
rows={Dogs}
columns={GrillaConfiguracion}
ShowDelete="true"
Export="true"
DeleteId={id => console.log(id)}
isLoading={isCargando}
ConfigurationId="id"
/>
</Route>
<Route path="/GrillaCompletaPrueba">
<GrillaCompleta></GrillaCompleta>
</Route>
<Route path="/topics">
<Topics />
</Route>
<Route path="/TestSocialReducer">
<TestSocialReducer></TestSocialReducer>
</Route>
<Route path="/">
<h2>Home</h2>
</Route>
</Switch>
</div>
</Router>
);
}
export default App;
Here the problematic component it's called
<Route path="/rGrillaTest">
<button key="btnFindTest" id="btnFindTest" onClick={FindDogs}>
Ver Perros Test
</button>
<br></br>
<RGridTest
key="RGridTest"
Tittle="Grilla Dogs Test"
rows={Dogs}
columns={GrillaConfiguracion}
ShowDelete="true"
Export="true"
DeleteId={id => console.log(id)}
isLoading={isCargando}
ConfigurationId="id"
/>
</Route>
It is what it is, there's no more than that, no redux, no external packages, nothing.
I hope I was clear and thank you for your next answers