I'm creating a cart page for a booking site in React using Redux and MUI and I have a table in which the user can remove an item from the cart. I wanted to add a smooth animation for the tableRow deletion, but the useRef hook only works on the first tableRow selected. How can I dynamically select the right tableRow? Here's the full .tsx file for the cart page
import { Typography, TableContainer, Table, TableBody, TableHead, TableRow, TableCell, Paper, Stack, Grid, Button, IconButton } from "@mui/material";
import ShoppingCartCheckoutIcon from '@mui/icons-material/ShoppingCartCheckout';
import RemoveShoppingCartIcon from '@mui/icons-material/RemoveShoppingCart';
import { createTheme, ThemeProvider } from "@mui/material/styles";
import { NavBar } from "../components/navbar";
import { useSelector, useDispatch } from "react-redux";
import type { RootState } from "../redux/store";
import { clearCart, removeFromCart } from "../redux/slices/cartSlice";
import { useRef } from "react";
const customTheme = createTheme({
typography: {
fontFamily: [
'Satisfy',
'cursive'
].join(','),
}
})
export const CartPage = () => {
const tableRowRef = useRef<HTMLTableRowElement>(null);
const dispatch = useDispatch();
const cartItems = useSelector((state: RootState) => state.cart.cart);
const handleDeleteItem = (id: number) => {
const tableRow = tableRowRef.current;
if (tableRow) {
tableRow.style.animation = 'removeTableRow 1s';
setTimeout(() => {
tableRow.style.animation = '';
dispatch(removeFromCart(id));
}, 1000);
}
}
return (
<div>
<NavBar/>
<Stack direction='row' sx={{flexWrap: 'wrap'}} className='cartPage'>
<TableContainer className="scrollableTable" component={Paper} sx={{ width:'50vw', height: '92vh'}}>
<Table stickyHeader>
<TableHead>
<TableRow>
<TableCell sx={{ fontSize: '25px' }}><b>Where?</b></TableCell>
<TableCell sx={{ fontSize: '25px' }}><b>When?</b></TableCell>
<TableCell sx={{ fontSize: '25px' }}><b>Room</b></TableCell>
<TableCell sx={{ fontSize: '25px' }}><b>Price</b></TableCell>
<TableCell></TableCell>
</TableRow>
</TableHead>
<TableBody>
{cartItems.map((item) => (
<TableRow key={item.id} ref={tableRowRef}>
<TableCell sx={{ fontSize: '20px' }}>{item.name}</TableCell>
<TableCell sx={{ fontSize: '20px' }}>{item.dates}</TableCell>
<TableCell sx={{ fontSize: '20px' }}><i>{item.room}</i></TableCell>
<TableCell sx={{ fontSize: '20px' }}>${item.price}</TableCell>
<TableCell>
<IconButton
color='error'
onClick={() => {handleDeleteItem(item.id)}}
>
<ShoppingCartCheckoutIcon fontSize="large" />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<Grid container justifyContent='center' sx={{width: '40vw'}}>
<Stack direction='column' justifyContent='space-evenly'>
<ThemeProvider theme={customTheme}>
<Typography variant="h1" className="cartPageCheckoutTypography" sx={{ color: '#74BF8B', textAlign: 'center'}}>
Satisfied?
</Typography>
</ThemeProvider>
<Stack direction='row' spacing={1.5}>
<Button
variant="contained"
color='success'
size="large"
sx={{ backgroundColor: '#5DBF9A', color: 'white', fontSize: '20px', width: '13vw' }}>
Confirm purchase
</Button>
<Button
variant="contained"
color='success'
size="large"
sx={{ backgroundColor: '#91BE77', color: 'white', fontSize: '20px', width: '13vw' }}
href='/book'>
Continue booking
</Button>
<Button
variant="contained"
color='success'
size="large"
sx={{ backgroundColor: '#B9BD5C', color: 'white', fontSize: '20px', width: '13vw' }}
onClick={()=>{dispatch(clearCart())}}
endIcon={<RemoveShoppingCartIcon />}
>
Clear cart
</Button>
</Stack>
</Stack>
</Grid>
</Stack>
</div>
);
};
I tried with
const handleDeleteItem = (id: number) => {
const tableRow = tableRowRef.current;
if (tableRow) {
tableRow.style.animation = 'removeTableRow 1s';
setTimeout(() => {
tableRow.style.animation = '';
dispatch(removeFromCart(id));
}, 1000);
}
}
but it works for the first row only.
Can somebody help me?
Update your ref to use an array instead of null:
const tableRowRef = useRef<HTMLTableRowElement[]>([]); // I'm not sure about the type in typescript for this
Add the second parameter i.e. index in your cartItems.map()
method:
cartItems.map((item, idx)
Then update the tag:
<TableRow key={item.id} ref={(el) => (tableRowRef .current[idx] = el)}>
Also update your handleDeleteItem method:
const handleDeleteItem = (id: number, idx: number) => {
const tableRow = tableRowRef.current[idx];
if (tableRow) {
tableRow.style.animation = "removeTableRow 1s";
setTimeout(() => {
tableRow.style.animation = "";
dispatch(removeFromCart(id));
}, 1000);
}
};