useEffect infinite loop WITH empty dependency array - reactjs

I am dispatching a thunk from my useEffect to populate a table when the page loads. The useEffect keeps running infintely, even though I am using an empty dependency array. Here is my code:
const Cart = (props) => {
const [didFetch, setDidFetch] = useState(false);
useEffect( () => {
async function fetchCart(){
await props.getCartItems();
setDidFetch(true);
}
fetchCart()
return setDidFetch(false)
},[]);
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Product</TableCell>
<TableCell align="right">Quantity</TableCell>
<TableCell align="right">Total Cost</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.cartItems.map((item) => (
<TableRow
key={item.product.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{item.product.name}
</TableCell>
<TableCell align="right">{item.quantity}</TableCell>
<TableCell align="right">
{(item.quantity * item.product.price).toFixed(2)}
</TableCell>
<TableCell>
<Button onClick={() => props.removeCartItem(item.id)}>
Remove
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};

The cleanup function of useEffect should be an anonymous function, and you shouldn't set state variables inside it, as #windmaomao pointed out, it is a memory leak:
Suggestion
If you want to store a didFetch flag, I would use a ref for this purpose:
import {useRef} from 'react'
const Cart = (props) => {
const fetched = useRef()
useEffect( () => {
async function fetchCart(){
const res = await props.getCartItems();
if(res) fetched.current = true
}
fetchCart()
return () => fetched.current = false
},[]);
return (
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Product</TableCell>
<TableCell align="right">Quantity</TableCell>
<TableCell align="right">Total Cost</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.cartItems.map((item) => (
<TableRow
key={item.product.name}
sx={{ "&:last-child td, &:last-child th": { border: 0 } }}
>
<TableCell component="th" scope="row">
{item.product.name}
</TableCell>
<TableCell align="right">{item.quantity}</TableCell>
<TableCell align="right">
{(item.quantity * item.product.price).toFixed(2)}
</TableCell>
<TableCell>
<Button onClick={() => props.removeCartItem(item.id)}>
Remove
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
};

Using your existing code it would be either:
Anonymous function
const [didFetch, setDidFetch] = useState(false);
useEffect( () => {
async function fetchCart(){
await props.getCartItems();
setDidFetch(true);
}
fetchCart()
return () => {
setDidFetch(false);
};
},[]);
Named Function
const [didFetch, setDidFetch] = useState(false);
useEffect( () => {
async function fetchCart(){
await props.getCartItems();
setDidFetch(true);
}
fetchCart()
return function cleanup() {
setDidFetch(false);
};
},[]);
Here are the useEffect docs for reference: https://reactjs.org/docs/hooks-effect.html

Related

My useEffect is not working. It is not getting called

I tried to get the users data through a useEffect and useEffect is not getting called. This is my allUsers.js file. Backend API is working and the UserService file is also working fine. I am only getting the first console log printed as mg. other console logs are not being printed.
export default function ViewUsers() {
const [userList, setUserList] = useState('');
console.log(userList,"mg")
useEffect(() => {
handleUser()
console.log("sas")
}, [])
const handleUser = async () =>{
const users = await UserService.viewUser()
setUserList(users);
console.log(userList,"user")
}
console.log(userList ,"1gmg")
return (
<>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Name</TableCell>
<TableCell align="right">Email</TableCell>
<TableCell align="right">Birth date</TableCell>
</TableRow>
</TableHead>
<TableBody>
{userList.map((row) => (
<TableRow
key={row.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.name}
</TableCell>
<TableCell align="right">{row.email}</TableCell>
<TableCell align="right">{row.birthDate}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}

reactjs conditional statement inside TableCell

How do I input conditional if else statement in a TableCell for ReactJS. I want the data table to output nothing is found if the search box returns nothing.
Here is the code:
export default function DataTable () {
const [query, setQuery] = useState('')
const [tableData, setTableData] = useState([])
useEffect( function () {
const fetchUsers = async function () {
const response = await axios.get(`/search/?query=${query}`)
setTableData(response.data)
}
if (query.length === 0 || query.length > 2) fetchUsers()
}, [query])
return (
<>
<input type={'text'} placeholder={'Enter Teacher ID Number....'} className='search-bar' onChange={(event) => setQuery(event.target.value)} />
<TableContainer component={Paper} className='data-table'>
<Table sx={{minWidth: 650}} aria-label='simple table'>
<TableHead>
<TableRow>
<TableCell className='tableCell'>ID</TableCell>
<TableCell className='tableCell'>Name</TableCell>
<TableCell className='tableCell'>Surname</TableCell>
<TableCell className='tableCell'>Title</TableCell>
<TableCell className='tableCell'>Email</TableCell>
<TableCell className='tableCell'>Action #1</TableCell>
<TableCell className='tableCell'>Action #2</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ tableData.map((data) => (
<TableRow key={data.id} sx={{ '&:last-child td, &:last-child th': {border:0}}}>
<TableCell className='tableCell'> { data.id }</TableCell>
<TableCell className='tableCell'> { data.name }</TableCell>
<TableCell className='tableCell'> { data.surname }</TableCell>
<TableCell className='tableCell'> { data.title }</TableCell>
<TableCell className='tableCell'> { data.email}</TableCell>
<TableCell className='tableCell'>
<button className={'verify'}>
<Link to={`/search/${data.id}`} className={'verify'} state={data}>Verify</Link>
</button>
</TableCell>
<TableCell className='tableCell'>
<button className={`drop`}>
<Link to={`/drop/${data.id}`} className={'drop'} state={data}>Drop</Link>
</button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}
I tried to input ternary operator '?' but it returns the first result
First of all I recommend to you separate the mapped Row in a separated component to handle easily and get the code more readable. This component could be in other file.
On the other hand I highly recommend if you are consuming the data from rest service, is using React Query.
export default function DataTable () {
const [query, setQuery] = useState('')
const [tableData, setTableData] = useState([])
useEffect( function () {
const fetchUsers = async function () {
const response = await axios.get(`/search/?query=${query}`)
setTableData(response.data)
}
if (query.length === 0 || query.length > 2) fetchUsers()
}, [query])
function UserTableRow(user){
return (
<TableRow key={user.id} sx={{ '&:last-child td, &:last-child th': {border:0}}}>
<TableCell className='tableCell'> { user.id }</TableCell>
<TableCell className='tableCell'> { user.name }</TableCell>
<TableCell className='tableCell'> { user.surname }</TableCell>
<TableCell className='tableCell'> { user.title }</TableCell>
<TableCell className='tableCell'> { user.email}</TableCell>
<TableCell className='tableCell'>
<button className={'verify'}>
<Link to={`/search/${user.id}`} className={'verify'} state={data}>Verify</Link>
</button>
</TableCell>
<TableCell className='tableCell'>
<button className={`drop`}>
<Link to={`/drop/${user.id}`} className={'drop'} state={data}>Drop</Link>
</button>
</TableCell>
</TableRow>
)
}
return (
<>
<input type={'text'} placeholder={'Enter Teacher ID Number....'} className='search-bar' onChange={(event) => setQuery(event.target.value)} />
<TableContainer component={Paper} className='data-table'>
<Table sx={{minWidth: 650}} aria-label='simple table'>
<TableHead>
<TableRow>
<TableCell className='tableCell'>ID</TableCell>
<TableCell className='tableCell'>Name</TableCell>
<TableCell className='tableCell'>Surname</TableCell>
<TableCell className='tableCell'>Title</TableCell>
<TableCell className='tableCell'>Email</TableCell>
<TableCell className='tableCell'>Action #1</TableCell>
<TableCell className='tableCell'>Action #2</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ tableData?.map((user) => <UserTableRow user={user}/>)}
{!tableData.length && <div>No results found.</div>}
</TableBody>
</Table>
</TableContainer>
</>
)
}

How to add pagination for Collapsible table mui in reactjs

I have an Collapsible table example here. But I don't know that how to add pagination for it. Any one have an idea ? Tks for all the help!
Example: https://codesandbox.io/s/p0td1n?file=/demo.tsx
Adding pagination to the table has no effect on being collapsible. So you can add it like this:
export default function CollapsibleTable() {
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const currentRows = rows.filter((r, ind) => {
return ind >= rowsPerPage * page && ind < rowsPerPage * (page + 1);
});
const handleChangePage = (event: unknown, newPage: number) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (
event: React.ChangeEvent<HTMLInputElement>
) => {
setRowsPerPage(parseInt(event.target.value, 10));
setPage(0);
};
return (
<TableContainer component={Paper}>
<Table aria-label="collapsible table">
<TableHead>
<TableRow>
<TableCell />
<TableCell>Dessert (100g serving)</TableCell>
<TableCell align="right">Calories</TableCell>
<TableCell align="right">Fat (g)</TableCell>
<TableCell align="right">Carbs (g)</TableCell>
<TableCell align="right">Protein (g)</TableCell>
</TableRow>
</TableHead>
<TableBody>
{currentRows.map((row) => (
<Row key={row.name} row={row} />
))}
</TableBody>
</Table>
<TablePagination
rowsPerPageOptions={[5, 10, 25]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</TableContainer>
);
}
You can take a look at this sandbox for a live working example of collapsible table with pagination.

React - How to break a line in a cell of a material ui table

I'm using the DenseTable component of the ui material and wanted to break a string into parts
here is my table:
I want in the cursos column, in the line that has LEIT - 1st Ano/LEE - 1st Ano I want to break the string where there is the '/'
below my DenseTable component code:
function createData(aulaID, numAula, tipoAula, nome, curso, data, acao) {
return { aulaID, numAula, tipoAula, nome, curso, data, acao };
}
export default function DenseTable() {
const { selectedRow, setSelectedRow, cursoSigla } = useListarAulas();
const [rows, setRows] = React.useState([]);
const {
showLancarSumarioModal,
handleOpenLancarSumarioModal,
handleCloseLancarSumarioModal,
} = useEscreverSumario();
const {
aulas,
// docente,
} = useFiltrarAulas();
React.useEffect(() => {
if (rows.length !== aulas.length) {
const tempRows = [];
aulas.map((aula) =>
tempRows.push(
createData(
aula.id,
aula.numero,
aula.tipo,
aula.disciplina.codigo,
cursoSigla(aula.cursos),
aula.data,
'Lançar Sumario'
)
)
);
setRows(tempRows);
}
}, [aulas, rows]);
return (
<>
<TableContainer component={Paper}>
<Table sx={{ minWidth: 650 }} size="small" aria-label="a dense table">
<TableHead>
<TableRow>
<TableCell>Disciplina</TableCell>
<TableCell align="right">Curso</TableCell>
<TableCell align="right">Data</TableCell>
<TableCell align="right">Acção</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.nome}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">
{row.nome}
</TableCell>
<TableCell align="right">{row.curso}</TableCell>
<TableCell align="right">
{row.data}
{/* moment(row.data).format('yyyy-MM-DD') */}
</TableCell>
<TableCell align="right">
<Button
size="small"
onClick={() => {
setSelectedRow(row);
handleOpenLancarSumarioModal();
}}
>
{row.acao}
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<EscreverSumarioModal
showModal={showLancarSumarioModal}
handleCloseModal={handleCloseLancarSumarioModal}
selectedRow={selectedRow}
/>
</>
);
}
the function cursoSigla() in useEffect receives a courses object where their respective acronyms are and returns a string concatenated with the acronym of each course like this "LEIT - 1º Ano/LEE - 2º Ano"
below the function code:
function cursoSigla(cursos) {
const sigla = [];
if (cursos.length > 1) {
if (sigla.length !== cursos.length) {
cursos.map((curso) => sigla.push(`${curso.sigla} - ${curso.ano}º Ano`));
return sigla.join('/');
}
}
if (cursos.length === 1) {
sigla.push(cursos[0].sigla);
return `${sigla[0]} - ${cursos[0].ano}º Ano`;
}
console.log(sigla);
return '';
}
My question is how to introduce a line break in my table cell ?
Instead of rendering them as:
<TableCell align="right">{row.curso}</TableCell>
You could do something like this instead:
<TableCell>
{row.curso.split("/").map((curs, idx) => (
<p key={idx}>{curs}</p>
))}
</TableCell>

Can't use wrapped fetch component in my .jsx

I have a MainComponentWrapper that is a fetch Get:
export default function MainComponentWrapper({ url, children }) {
const classes = useStyles()
const [data, setData] = React.useState()
React.useEffect(() => {
fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + window.localStorage.getItem("access_token"),
},
})
.then(resp => {
return resp.json()
})
.then(fetchedData => {
setData(fetchedData)
})
.catch(error => {
console.log(error)
window.localStorage.removeItem("access_token")
window.location.replace("/")
})
}, [url])
if (data === undefined) {
return (
<div className={classes.root}>
<CircularProgress className={classes.progress} />
</div>
)
}
return (
<div className={classes.root}>
{React.cloneElement(children, { data: data })}
</div>
)
}
I was trying to use it in my .jsx component so i could get data that i need by passing an api url, and print it on a table.
export default function Machines({data}) {
return (
<div className={classes.root}>
<h1>Azure machines</h1>
<Table className={classes.table} size="small">
<TableHead>
<TableRow>
<TableCell align="left">name</TableCell>
<TableCell align="left">resource_group</TableCell>
<TableCell align="left">location</TableCell>
<TableCell align="left">status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{data.map(row => (
<TableRow key={row.name + row.resource_group}>
<TableCell align="left" component="th" scope="row">
<StyledButton size = "small" className={style.size3}>
<Link style={{ color: 'inherit', textDecoration: 'inherit'}} to={`/machines/${row.resource_group + "/" + row.name}`}>{row.name}</Link>
</StyledButton>
</TableCell>
<TableCell align="left">{row.resource_group}</TableCell>
<TableCell align="left">{row.location}</TableCell>
<MainComponentWrapper url={"/api/machine/one"} >
{({ tmpData }) => (
<TableCell key= {row.location + row.name} log={console.log(data.status)} align="left">
<LoadingIndicator/>
<MachineStatusIcon
status_code={tmpData.status_code}
status={tmpData.status}
/>
</TableCell>
)}
</MainComponentWrapper>
</TableRow>
))}
</TableBody>
</Table>
</div>
)
}
The problem is I can't find a way to use it, I've checked some similar examples but no success.
If i inspect on google chrome and check the network tab i can see that the component is fetching the data, because the data is listed there, but how do i use it to pass as values to my table?
Your MainComponentWrapper is inside where you have 'data' defined. Your wrapper should envelope the entire JSX code which in this case is the table.
<MainComponentWrapper url={"/api/machine/one"} >
<TableBody>
<!-- data is being used here but it didn't exist -->
{data.map(row => (
<TableRow key={row.name + row.resource_group}>
<TableCell align="left" component="th" scope="row">
<StyledButton size = "small" className={style.size3}>
<Link style={{ color: 'inherit', textDecoration: 'inherit'}} to={`/machines/${row.resource_group + "/" + row.name}`}>{row.name}</Link>
</StyledButton>
</TableCell>
<TableCell align="left">{row.resource_group}</TableCell>
<TableCell align="left">{row.location}</TableCell>
{({ tmpData }) => (
<TableCell key= {row.location + row.name} log={console.log(data.status)} align="left">
<LoadingIndicator/>
<MachineStatusIcon
status_code={tmpData.status_code}
status={tmpData.status}
/>
</TableCell>
)}
</TableRow>
))}
</TableBody>
</MainComponentWrapper>

Resources