React - Why Props do not update - javascript

I just noticed that the props I send for my PayPal Sandbox are not updated.
Context:
I have an add cart page where the user can increase / decrease the quantity of a product they have in their cart.
I also display the total amount of the cart.
These two values: Quantity and Total are sent by props to the PayPal component (A button).
Except that if the user changes the quantity, these new values are not taken into account by my PayPal button. So I have to manually refresh the page. Which is a huge problem.
Here is my code:
export default function PanierPage() {
let MonTotal = '';
const classPanier = PanierStyles();
const [total, setTotal] = useState([])
// Récupération des détails des paniers
const [paniers, setPaniers] = useState([])
const getPaniersDetails = () => [
getDetails(JSON.parse(localStorage.getItem('User')).id).then(response => {
setPaniers(response.data)
}).catch(err => console.log(err))
]
const handleIncrement = (id, quantite) => {
updateQuantite(id, quantite + 1 ).then(response => {
getDetails(JSON.parse(localStorage.getItem('User')).id).then(response => {
setPaniers(response.data)
}).catch(err => console.log(err))
getTotal(JSON.parse(localStorage.getItem('User')).id).then(response => {
setTotal(response.data)
}).catch(err => console.log(err))
}).catch(err => console.log(err))
}
const handleDecrement = (id, quantite) => {
updateQuantite(id, quantite - 1 ).then(response => {
getDetails(JSON.parse(localStorage.getItem('User')).id).then(response => {
setPaniers(response.data)
}).catch(err => console.log(err))
getTotal(JSON.parse(localStorage.getItem('User')).id).then(response => {
setTotal(response.data)
}).catch(err => console.log(err))
}).catch(err => console.log(err))
}
const SupprPanier = (id) => {
deletePanier(id).then(response => {
getDetails(JSON.parse(localStorage.getItem('User')).id).then(response => {
setPaniers(response.data)
}).catch(err => console.log(err))
getTotal(JSON.parse(localStorage.getItem('User')).id).then(response => {
setTotal(response.data)
}).catch(err => console.log(err))
}).catch(err => console.log(err))
}
// Affichage des détails des paniers
const paniersAffichage = paniers.map((panier) => (
<Grid container style={{justifyContent:'center', padding: '1em'}}>
<Card key={panier.PAN_ID} style={{height: '100%', display: 'flex', flexDirection: 'row'}} variant='outlined'>
<CardHeader title={panier.PRO_NOM}/>
<CardMedia style={{height: 150, width: 150, marginLeft: 'auto', marginRight: 'auto'}} image={`http://localhost:8090/${panier.PRO_URL}`}/>
<Grid direction="row" justify="center" alignItems="center">
<CardContent>
{panier.PAN_QUANTITE === panier.PRO_QTESTOCK
?
<Button variant='outlined' color="primary" disabled onClick={() => handleIncrement(panier.PAN_ID, panier.PAN_QUANTITE)}> + </Button>
:
<Button variant='outlined' color="primary" onClick={() => handleIncrement(panier.PAN_ID, panier.PAN_QUANTITE)}> + </Button>
}
<Typography style={{textAlign: 'center'}}>
{panier.PAN_QUANTITE}
</Typography>
{panier.PAN_QUANTITE === 1
?
<Button variant='outlined' color="secondary" disabled onClick={() => handleDecrement(panier.PAN_ID, panier.PAN_QUANTITE)}> - </Button>
:
<Button variant='outlined' color="secondary" onClick={() => handleDecrement(panier.PAN_ID, panier.PAN_QUANTITE)}> - </Button>
}
</CardContent>
</Grid>
<Box>
<Grid container direction="row" justify="center" alignItems="center" >
<Typography style={{textAlign: 'center'}}>
{panier.PRO_PRIX} €
</Typography>
<CardActions>
<Button onClick={() => SupprPanier(panier.PAN_ID)} className={classPanier.iconDelete}><DeleteIcon/></Button>
</CardActions>
</Grid>
</Box>
</Card>
</Grid>
));
const PanierTotal = () => {
getTotal(JSON.parse(localStorage.getItem('User')).id).then(response => {
setTotal(response.data)
}).catch(err => console.log(err))
}
const TotalAffiche = total.map((total) => (
<Grid style={{color: 'white', marginTop: '2%', marginBottom: '2%',}}>
Montant total de votre panier : {total.TOTAL} €
</Grid>
))
// Chargement des produits
useEffect(() => {
getPaniersDetails();
PanierTotal();
}, [])
return (
<>
{paniersAffichage}
<Grid container style={{justifyContent:'center'}}>
{TotalAffiche}
<Grid style={{marginLeft: '2%'}}>
{(total.length > 0 && paniers.length > 0) && <PayPal prixTotal={total[0].TOTAL} paniers={paniers} />}
</Grid>
</Grid>
</>
);
}
And my PayPal button :
export default function Paypal({prixTotal, paniers})
{
const PDFGeneration = () => {
var doc = new jsPDF('p','px','a4','false');
var lesPaniers = paniers.map(panier => [panier.PRO_NOM, panier.PAN_QUANTITE, panier.PRO_PRIX ]);
doc.text(10,15,'Résumé de votre commande : ');
doc.autoTable({
theme: 'grid',
startY: 20,
head: [['Nom', 'Quantité', 'Prix (€)']],
body: lesPaniers,
});
doc.text(10,120,`Total de la commande : ${prixTotal} €`);
doc.text(10,150,'A la prochaine fois !');
doc.save('CommandeInfo.pdf');
}
const paypal = useRef()
useEffect(() => {
window.paypal.Buttons({
style: {
color: 'gold',
},
createOrder: (data, actions) => {
return actions.order.create({
intent: 'CAPTURE',
purchase_units: [
{
description: 'Transaction du site E-Commerce.',
amount: {
currency_code: 'EUR',
value: prixTotal
}
}]
})
},
onApprove: (data, actions) => {
createCommande(true).then(response => {
PDFGeneration();
window.location.reload(false);
});
return actions.order.capture();
},
onCancel: (data, actions) => {
console.log("Transaction annulé");
console.log(prixTotal)
}
}).render(paypal.current)
}, [])
return (
<div>
<div ref={paypal}></div>
</div>
);
}
However, each product in the shopping cart is assigned a key that NORMALLY should trigger a re-render of the values for PayPal.
If anyone can explain/find me the problem, I would be grateful! Thanks again for your time.

In your PayPal component you do not pass any list to the useEffect hook as a second argument. That way it will be executed only when the component is mounted. If you want to update it when the props change you should pass the props that need to trigger it as a second argument list:
export default function Paypal({prixTotal, paniers})
{
// ...
useEffect(() => {
if (window.paypalBtn) window.paypalBtn.close();
window.paypalBtn = window.paypal.Buttons({
// ...
});
window.paypalBtn.render(paypal.current)
}, [prixTotal, paniers]);
// ...
}

Related

Got the error invalid input syntax for integer

Faced a problem with incorrect syntax when executing the get method to the database. The error is due to an attempt to put string data into integer, but I can't figure out where it's happening.
I think this is happening somewhere in the code below, please help me find where:
`
const Admin = () => {
const [fumoVisible, setFumoVisible] = useState(false);
const [searchFumo, setSearchFumo] = useState("");
const [searchedFumo, setSearchedFumo] = useState([]);
useEffect(() => {
getAllFumoInAdminPage(searchFumo, currentPage, filter).then(
({ count, rows }) => {
setSearchedFumo(rows);
setCount(count);
}
);
}, [currentPage]);
useEffect(() => {
getAllFumoInAdminPage(searchFumo, 1, filter).then(({ count, rows }) => {
setSearchedFumo(rows);
setCount(count);
setCurrentPage(1);
});
}, [filter]);
const fetchFumo = () => {
getAllFumoInAdminPage(searchFumo, currentPage, filter).then(
({ count, rows }) => {
setSearchedFumo(rows);
setCount(count);
}
);
};
return (
<Container className="d-flex flex-column">
<InputGroup className="mb-3">
<Form.Control
aria-label="Default"
aria-describedby="inputGroup-sizing-default"
value={searchFumo}
onChange={(e) => setSearchFumo(e.target.value)}
placeholder="Enter name..."
/>
<Button onClick={fetchFumo} variant="outline-dark" className="ml-2">
Search
</Button>
</InputGroup>
<ListGroup>
{searchedFumo &&
searchedFumo.map(({ id, img, price, name }) => {
return (
<ListGroup.Item className="mt-3" key={id}>
//
</ListGroup.Item>
);
})}
</ListGroup>
</Container>
);
};
export default Admin;
`

How can I make onClick events work with specific elements in a .map() function?

I'm trying to make a list of users inside a chat. Each user is supposed to have a button opening a menu, allowing admins to ban that specific user. I'm mapping an array of users to get the list, but the issue is that whenever I try to click on the 'ban' button, the user data seems to always be for the last user in the array, instead of the user on which I clicked. Here's the code for the entire component:
const UserList: React.FC<{channel: string}> = ({channel}) => {
const [channelMembers, setChannelMembers] = useState<any[] | null>(null)
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
const [isAdmin, setIsAdmin] = useState(false)
const navigate = useNavigate()
const open = Boolean(anchorEl)
const baseUrl = useContext(UrlContext)
useEffect(() => {
axios.get(baseUrl + `chat/${channel}/users`, {withCredentials: true}).then((response) => {
setChannelMembers(response.data)
})
}, [baseUrl, channel, anchorEl])
useEffect(() => {
axios.get(baseUrl + `users/me`, {withCredentials: true}).then((response) => {
axios.get(baseUrl + `chat/${channel}/admins`, {withCredentials: true}).then((resp2) => {
if (resp2.data.includes(response.data.id)) {
setIsAdmin(true)
}
}).catch((error) => {
console.log(error)
})
}).catch((error) => {
console.log(error)
})
}, [baseUrl, anchorEl])
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => {
setAnchorEl(null)
}
const handleBan = (user: any) => () => {
let banUser = {...user}
console.log(banUser)
axios.post(baseUrl + `chat/${channel}/ban`, banUser, {withCredentials: true}).catch((error) => {
console.log(error)
})
handleClose()
}
return (
<>
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: drawerWidth,
boxSizing: 'border-box',
},
}}
variant="permanent"
anchor="right"
>
<Toolbar />
<List>
{channelMembers?.map((user: any, index: number) => {
return (
<Fragment key={index}>
<ListItem>
<ListItemText primary={user.name} />
<ListItemIcon>
{(user.status === Status.ONLINE) ? <CircleIcon style={{color: "green"}} fontSize="small" /> : <RadioButtonUncheckedIcon style={{color: "grey"}} fontSize="small" />}
</ListItemIcon>
<IconButton
aria-label="more"
id="long-button"
aria-controls={open ? 'long-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-haspopup="true"
onClick={handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu
id="long-menu"
MenuListProps={{
'aria-labelledby': 'long-button'
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
{isAdmin &&
[<MenuItem onClick={handleClose}>
<Typography>
Make admin
</Typography>
</MenuItem>,
<MenuItem onClick={handleClose}>
<Typography>
Mute
</Typography>
</MenuItem>,
<MenuItem onClick={handleClose}>
<Typography>
Kick
</Typography>
</MenuItem>,
<MenuItem onClick={handleBan(user)}>
<Typography>
Ban
</Typography>
</MenuItem>]}
<MenuItem onClick={handleClose}>
<Typography>
View profile
</Typography>
</MenuItem>
<MenuItem onClick={handleClose}>
<Typography>
Invite to play
</Typography>
</MenuItem>
</Menu>
</ListItem>
<hr />
</Fragment>
)
} )}
</List>
</Drawer>
</>
)
}
You're rendering multiple menus in your map and since you're using only one state when opening one menu it sets open to true which means all menus are open because of this prop open={open}.
You should try to render only one Menu and possibly store in a state the selected user. There's probably a better way to do this but it should solve your problem.
You're calling the ban handleBan(user) instead of () => handleBan(user), function instead and using the return value of it.
Try this code
const UserList: React.FC<{ channel: string }> = ({ channel }) => {
const [channelMembers, setChannelMembers] = useState<any[] | null>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [isAdmin, setIsAdmin] = useState(false);
const navigate = useNavigate();
const open = Boolean(anchorEl);
const baseUrl = useContext(UrlContext);
useEffect(() => {
axios
.get(baseUrl + `chat/${channel}/users`, { withCredentials: true })
.then((response) => {
setChannelMembers(response.data);
});
}, [baseUrl, channel, anchorEl]);
useEffect(() => {
axios
.get(baseUrl + `users/me`, { withCredentials: true })
.then((response) => {
axios
.get(baseUrl + `chat/${channel}/admins`, { withCredentials: true })
.then((resp2) => {
if (resp2.data.includes(response.data.id)) {
setIsAdmin(true);
}
})
.catch((error) => {
console.log(error);
});
})
.catch((error) => {
console.log(error);
});
}, [baseUrl, anchorEl]);
const handleClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleBan = (user: any) => () => {
let banUser = { ...user };
console.log(banUser);
axios
.post(baseUrl + `chat/${channel}/ban`, banUser, { withCredentials: true })
.catch((error) => {
console.log(error);
});
handleClose();
};
return (
<>
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box"
}
}}
variant="permanent"
anchor="right"
>
<Toolbar />
<List>
{channelMembers?.map((user: any, index: number) => {
return (
<Fragment key={index}>
<ListItem>
<ListItemText primary={user.name} />
<ListItemIcon>
{user.status === Status.ONLINE ? (
<CircleIcon style={{ color: "green" }} fontSize="small" />
) : (
<RadioButtonUncheckedIcon
style={{ color: "grey" }}
fontSize="small"
/>
)}
</ListItemIcon>
<IconButton
aria-label="more"
id="long-button"
aria-controls={open ? "long-menu" : undefined}
aria-expanded={open ? "true" : undefined}
aria-haspopup="true"
onClick={handleClick}
>
<MoreVertIcon />
</IconButton>
<Menu
id="long-menu"
MenuListProps={{
"aria-labelledby": "long-button"
}}
anchorEl={anchorEl}
open={open}
onClose={handleClose}
>
{isAdmin && [
<MenuItem onClick={handleClose}>
<Typography>Make admin</Typography>
</MenuItem>,
<MenuItem onClick={handleClose}>
<Typography>Mute</Typography>
</MenuItem>,
<MenuItem onClick={handleClose}>
<Typography>Kick</Typography>
</MenuItem>,
<MenuItem onClick={() => handleBan(user)}>
<Typography>Ban</Typography>
</MenuItem>
]}
<MenuItem onClick={handleClose}>
<Typography>View profile</Typography>
</MenuItem>
<MenuItem onClick={handleClose}>
<Typography>Invite to play</Typography>
</MenuItem>
</Menu>
</ListItem>
<hr />
</Fragment>
);
})}
</List>
</Drawer>
</>
);
};
Instead of doing this:
onClick={handleBan(user)}
Pass an anonymous function to your on click, so you can pass the correct user like this:
onClick={() => handleBan(user)}
Here's a code sandbox showing how the 2 approaches differ.
Here's the answer that works

How to make a form for add and edit with Reack-hook-form

I am trying to create a single form able to manage the creation of a product but also to edit it (by passing the values through an object).
The form works perfectly to create a product but how to make it able to edit a product?
I tried to pass my values via "value" or "defaultValue" but it seems to be a problem with the creation of a product (The values are as saved)
The idea of creating a second form with default values passed in doesn't seem right
All this to create a basic CRUD.
Here is my form, I use Material-UI for the form elements, especially for its modal
export default function App() {
// ReactHookForm
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const [Produits, setProduits] = useState([]);
const [openAjout, setOpenAjout] = useState(false);
const [openModif, setOpenModif] = useState({open: false, produit: null});
const [openSuppr, setOpenSuppr] = useState({open: false, produitId: null});
// Load product.
const Chargement = async () => {
const res = await fetch(`${ENDPOINT}/api/produits`);
const data = await res.json();
setProduits(data);
};
// create new product.
const onSubmit = async (data) => {
const re = await axios.post(`${ENDPOINT}/api/produits`, data, {
headers: { 'Content-Type': 'application/json'}
})
setProduits([...Produits, data])
};
// edit product.
const onSubmitUpdate = async (data) => {
console.log('data', data)
// const re = await axios.put(`${ENDPOINT}/api/produits/${data}`, data, {
// headers: { 'Content-Type': 'application/json'}
// })
};
// delete product.
const confirmSuppr = async (data) => {
setOpenSuppr({open: false, produitId: data});
const re = await axios.delete(`${ENDPOINT}/api/produits/${data}`, data, {
headers: { 'Content-Type': 'application/json'}
})
setProduits(prevState => {
prevState.filter(Produits => {
return Produits.pro_id !== data;
})
});
}
// -- Handler open / close Dialogs
// Handlers des Dialogs
const handleAjout = (Bool) => {
setOpenAjout(Bool);
};
const handleModif = (Bool, Produit) => {
setOpenModif({open: Bool, produit: Produit});
};
const handleSuppr = (Bool, ProduitID) => {
setOpenSuppr({open: Bool, produitId: ProduitID});
};
// Rendering
useEffect(() => {
Chargement();
}, []);
return (
<div>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow style={{ background: "#cad2c5" }}>
<TableCell align="center">{"ID"}</TableCell>
<TableCell align="center">{"Nom"}</TableCell>
<TableCell align="center">{"Description"}</TableCell>
<TableCell align="center">{"Instruction"}</TableCell>
<TableCell align="center">{"Prix"}</TableCell>
<TableCell align="center">{"Action"}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{Produits?.map((produit) => (
<TableRow key={`PRO_${produit.pro_id}`}>
<TableCell align="center">{produit.pro_id}</TableCell>
<TableCell align="center">{produit.pro_nom}</TableCell>
<TableCell align="center">{produit.pro_description}</TableCell>
<TableCell align="center">{produit.pro_instruction}</TableCell>
<TableCell align="center">{produit.pro_prix}{' €'}</TableCell>
<TableCell align="center">
<Tooltip title="Modifier" placement="top">
<IconButton
style={{
background: "#ffca3a",
color: "white",
borderRadius: "10%",
}}
onClick={() => handleModif(true, produit)}
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="Supprimer" placement="top">
<IconButton
style={{
background: "#ff595e",
color: "white",
borderRadius: "10%",
}}
onClick={() => handleSuppr(true, produit.pro_id)}
>
<DeleteIcon />
</IconButton>
</Tooltip>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<Grid>
<Button
variant="contained"
endIcon={<AddIcon />}
onClick={() => handleAjout(true)}
>
{"Ajouter"}
</Button>
{/**
* Dialog add product
*/}
<Dialog open={openAjout} onClose={() => handleAjout(false)}>
<DialogTitle>{"Ajout d'un produit"}</DialogTitle>
<form onSubmit={handleSubmit(onSubmit)}>
<DialogContent>
<TextField
type={"text"}
name="pro_nom"
{...register("pro_nom", { required: true })}
label="Nom"
placeholder="Hamburger"
autoFocus
variant="outlined"
/>
{errors.pro_nom && <span>Champ requis.</span>}
<TextField
type={"text"}
name="pro_description"
{...register("pro_description", { required: true })}
label="Description"
placeholder="Description du produit"
variant="outlined"
/>
{errors.pro_description && <span>Champ requis.</span>}
<TextField
type={"text"}
name="pro_instruction"
{...register("pro_instruction")}
label="Instruction"
placeholder="Instruction particulière"
variant="outlined"
/>
<TextField
type={"number"}
name="pro_prix"
{...register("pro_prix", { required: true, min: 0, valueAsNumber: true })}
label="Prix"
placeholder="Obligatoire"
variant="outlined"
/>
{errors.pro_prix && <span>Champ requis.</span>}
</DialogContent>
<DialogActions>
<Button type="reset">{"Effacer"}</Button>
<Button onClick={() => handleAjout(false)}>{"Fermer"}</Button>
<Button type="submit">{"Confirmer"}</Button>
</DialogActions>
</form>
</Dialog>
There is a usefule function in react-hook-form called reset. It allows you to predefine the values for the form. Click here to see the documentation.
You just need to call it inside the useEffect hook and specify the fields.
But before that you need to specify a state to check if it is an "add" or "edit" action.
const [action, setAction] = useState();
useEffect(() => {
if(action == "edit"){
reset({
pro_description: "pro_description",
pro_instruction: "pro_instruction"
})
}else if(action == "add"){
reset({
pro_description: "",
pro_instruction: ""
})
}
}, [reset, action])

redux and react unwanted effect

Hello everyone 👋
I'm trying to make a cart functionality with redux.
2.Description of problem:
The problem is that as soon as I want to remove a product that is not the last from my basket.
Redux does delete the desired product from the store, but at the frontend I still see the product and react deletes the last product from the list (the one I didn't want to delete).
I have to go to another page without reloading and returning to the basket to see react to reset
If I remove the last product from the list the removal works as it should.
I have made a video link that are served by google drive:
https://drive.google.com/file/d/1miZA4B1Ay5OZZBGPj1bCcQHsGv21oVW_/view
Here is the redux effect I want or expected to have ( this is the action of removing the latest product from cart list :
If I have several products in my cart and I just want to delete a product without it being the last ( unwanted effect ):
The ADD_TO_CART_ACTION dispatch
store.dispatch({
type: ADD_PRODUCT_TO_CART_ACTION,
payload: {
data: {
productID: data.product.id,
attributeID: RadioState,
price: data.price,
quantity: 1
}
}
})
this is my cart reducer:
export function CartReducer(state = [], action){
const cart = [...state]
switch (action.type){
case 'ADD_PRODUCT_TO_CART_ACTION':
return [...state, {...action.payload.data}];
case 'UPDATE_QUANTITY_FROM_CART_ACTION':
return cart.map(product => {
if (product.attributeID === action.payload.attributeID){
product.quantity++
return {...product}
} else {
return product
}
})
case 'REMOVE_QUANTITY_FROM_CART_ACTION':
return cart.map(product => {
if (product.attributeID === action.payload.attributeID){
product.quantity--
return {...product}
} else {
return product
}
})
case 'TRASH_PRODUCT_FROM_CART_ACTION':
return cart.filter(product => product.attributeID !== action.payload)
default:
return state;
}
}
Here is the cart component who them is connected from redux first:
export function Cart (props)
{
const [productCount, setProductCount] = useState(0)
useEffect(() => {
setProductCount(props.count)
}, [])
if (props.cart.length === 0){
return <div className="home"><p className={'text-center text-green-500'}>Add product to cart.</p></div>
}
return (
<React.Fragment className="home">
<Grid container spacing={3}>
{props.cart.map((item, index) => {
return (
<Grid item xs={6}>
<ProductCartDetails productCount={productCount} key={index} attributes={item}/>
</Grid>
)
})}
</Grid>
</React.Fragment>
)
}
export const CartStore = connect(
(state) => ({
cart: cartSelectors(state),
count: cartCount(state)
})
)(Cart)
This is ProductCartDetails ( The card of the product, and here who action are dispatch )
export default function ProductCartDetails (props){
const [productCount, setProductCount] = useState(0)
const [product, setProduct] = useState([])
const [requestReady, setRequestReady] = useState(false)
useEffect(() => {
axios.get(`product/${props.attributes.productID}`)
.then(({data}) => {
setProduct(data)
setRequestReady(! requestReady)
})
.catch((err) => {
console.log(err)
})
}, [props.productCount])
useEffect(() => {
setProductCount(props.productCount)
}, [props.productCount])
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
width: "100%",
marginTop: 4,
backgroundColor: "#faf7f7",
boxSizing: 'border-box',
},
details: {
display: 'flex',
flexDirection: 'column',
},
content: {
flex: '1',
},
cover: {
width: 151,
height: '100%'
},
}));
const onClickAddMoreQuantity = () => {
let cart = [...store.getState()]
let updatedQuantity = false;
cart.map(product => {
if (product.attributeID === props.attributes.attributeID){
store.dispatch(
{
type: UPDATE_QUANTITY_FROM_CART_ACTION,
payload: {
attributeID: props.attributes.attributeID
}
}
)
updatedQuantity = true
}
})
if (updatedQuantity === false){
swal({
icon: 'error',
title: 'Cart',
text: 'Product quantity cannot be bigger than the product stock.',
})
}
}
const onClickRemoveQuantityFromCart = () => {
let cart = [...store.getState()]
let updatedQuantity = false;
cart.map(product => {
if (product.attributeID === props.attributes.attributeID){
store.dispatch(
{
type: REMOVE_QUANTITY_FROM_CART_ACTION,
payload: {
attributeID: props.attributes.attributeID
}
}
)
updatedQuantity = true
}
})
if (updatedQuantity === false){
swal({
icon: 'error',
title: 'Cart',
text: 'Product quantity has not been removed.',
})
}
}
const onClickTrashProductFromCart = () => {
let cart = [...store.getState()]
let updatedQuantity = false;
cart.map(product => {
if (product.attributeID === props.attributes.attributeID){
store.dispatch(
{
type: TRASH_PRODUCT_FROM_CART_ACTION,
payload: props.attributes.attributeID
}
)
updatedQuantity = true
}
})
if (updatedQuantity === false){
swal({
icon: 'error',
title: 'Cart',
text: 'Product has not been removed.',
})
}
}
const classes = useStyles();
if (productCount !== 0){
return (
<>
<Card className={classes.root}>
<Link to={requestReady ? `/details/${product.slug}` : null}>
<img
className={classes.cover}
src={requestReady ? axios.defaults.baseURL+product.image[0].url+"?h=600" : null}
alt="image cover product cart"
/>
</Link>
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography className="text-center text-gray-700" component="h6" variant="h6">
{requestReady ? product.name : null}
</Typography>
<p className="text-center text-gray-600">
Details Of Product
</p>
<div>
<Typography variant="subtitle1" color="textSecondary">
Category: {requestReady ? product.category.label : null}
</Typography>
<Typography variant="subtitle1" color="textSecondary">
<ProductCartAttributeDetails attributes={props.attributes} />
</Typography>
</div>
</CardContent>
<CardActions>
<button id={requestReady ? product.id : null} onClick={onClickAddMoreQuantity}>
<Add height={10} />Add quantity
</button>
<button>
<Delete height={10} onClick={onClickTrashProductFromCart} />Trash
</button>
<button onClick={onClickRemoveQuantityFromCart}>
<Remove height={10} />Remove quantity
</button>
</CardActions>
</div>
</Card>
</>
)
} else {
return (
<>
<Card className={classes.root}>
<Link to={requestReady ? `/details/${product.slug}` : null}>
<img
className={classes.cover}
src={requestReady ? axios.defaults.baseURL+product.image[0].url+"?h=600" : null}
alt="image cover product cart"
/>
</Link>
<div className={classes.details}>
<CardContent className={classes.content}>
<Typography className="text-center text-gray-700" component="h6" variant="h6">
{requestReady ? product.name : null}
</Typography>
<p className="text-center text-gray-600">
Details Of Product
</p>
<div>
<Typography variant="subtitle1" color="textSecondary">
Category: {requestReady ? product.category.label : null}
</Typography>
<Typography variant="subtitle1" color="textSecondary">
<ProductCartAttributeDetails attributes={props.attributes} />
</Typography>
</div>
</CardContent>
<CardActions>
<button id={requestReady ? product.id : null} onClick={onClickAddMoreQuantity}>
<Add height={10} />Add quantity
</button>
<button>
<Delete height={10} onClick={onClickTrashProductFromCart} />Trash
</button>
<button onClick={onClickRemoveQuantityFromCart}>
<Remove height={10} />Remove quantity
</button>
</CardActions>
</div>
</Card>
</>
)
}
}
And the ProductCartAttributeDetails if needed
export default function ProductCartAttributeDetails({attributes}){
const [attribute, setAttribute] = useState([])
const [requestReady, setRequestReady] = useState(false)
useEffect(() => {
axios.get(`attributes/${attributes.attributeID}`)
.then(({data}) => {
setAttribute(data)
setRequestReady(! requestReady)
})
.catch((err) => {
console.log(err)
})
}, [])
return (
<>
<Typography variant="subtitle1" color="textSecondary">
<p><span className="capitalize">{requestReady ? attribute.attribute : null}</span> : {requestReady ? attribute.value : null}</p>
</Typography>
<Typography variant="subtitle1" color="textSecondary">
<p>Quantity: {requestReady ? attributes.quantity : null}</p>
</Typography>
<Typography variant="subtitle1" color="textSecondary">
<p>Total Price: {requestReady ? attribute.price * attributes.quantity : null}</p>
</Typography>
</>
)
}
Problem was fixed by changing the default index of react js by your own index, for me i have use the index that i receive from my store to be sure them is unique

React freeze for 1~2 seconds when use map in a state array of 168 objects

My problem occurs when I try to update and property from a state array of objects after executing a axios.put.
That's my code:
States - useEffect
//...
const [volunteers, setVolunteers] = useState([]);
//...
useEffect(() => {
async function loadVolunteers() {
api
.get('volunteers')
.then(response => {
const data = response.data.map(volunteer => ({
...volunteer,
notChecked: volunteer.confirmed === null,
confirmedText: volunteer.confirmed ? 'Deferido' : 'Indeferido',
createdDateFormatted: format(
parseISO(volunteer.createdAt),
"d 'de' MMMM'/'yyyy",
{
locale: pt,
}
),
}));
setVolunteers(data);
})
.then(() => {
api
.get('departments')
.then(response => {
setDepartments(response.data);
setLoading(false);
})
.catch(() => {
toast.error(
'Ops! Aconteceu algum problema ao buscar as Inscrições. Tente novamente mais tarde e/ou contate o Programador'
);
});
})
.catch(() => {
toast.error(
'Ops! Aconteceu algum problema ao buscar as Inscrições. Tente novamente mais tarde e/ou contate o Programador'
);
});
}
if (!isAdmin) {
history.push('/dashboard');
}
loadVolunteers();
dispatch(changePage('inscricoes'));
}, []); // eslint-disable-line
//...
const updateDepartment = async (id, department_id) => {
try {
await api.put(`volunteers/${id}/department`, {
department_id,
});
setVolunteers(
volunteers.map(volunteer => {
if (volunteer.id === id) {
volunteer.department_id = department_id;
return volunteer;
}
return volunteer;
})
);
toast.success('Departamento atualizado');
} catch (err) {
toast.error('Erro ao atualizar departamento');
}
};
If I remove the setVolunteers instruction, this methods runs instantly.
But when I try to do that map in the volunteers array/state, the application freeze for 1~2 seconds.
Why?
Below the rest of the react code iterating the volunteers array.
{volunteers.map((volunteer, index) => (
<Card
key={volunteer.id}
className={classes.card}
variant="outlined"
>
<CardContent>
<TextField
variant="outlined"
margin="none"
required
select
fullWidth
id="gender"
value={volunteer.department_id}
placeholder="Selecione"
onChange={e =>
updateDepartment(volunteer.id, e.target.value)
}
label="Selecione seu sexo"
name="gender"
>
{departments.map(option => (
<MenuItem key={option.id} value={option.id}>
{option.name}
</MenuItem>
))}
</TextField>
<Typography variant="h5" component="h2">
{volunteer.user.name}
</Typography>
<Typography className={classes.pos} color="textSecondary">
{volunteer.user.phone}
</Typography>
<Typography variant="body2" component="p">
<strong>Líder: </strong>
{volunteer.user.leader_name}
</Typography>
</CardContent>
<CardActions className={classes.subActions}>
{volunteer.notChecked ? (
<>
<Button
size="small"
fullWidth
variant="contained"
color="primary"
disabled={loadConfirm}
onClick={() =>
handleConfirmation(index, volunteer.id, true)
}
>
Deferido
</Button>
<Button
size="small"
fullWidth
variant="contained"
disabled={loadConfirm}
onClick={() =>
handleConfirmation(index, volunteer.id, false)
}
>
Indeferido
</Button>
</>
) : (
<Button
size="small"
fullWidth
variant="contained"
color="primary"
disabled
>
{volunteer.confirmed ? 'Deferido' : 'Indeferido'}
</Button>
)}
<Button
size="small"
fullWidth
variant="contained"
className={classes.deny}
disabled={loadConfirm}
onClick={() => handleDelete(index, volunteer.id, true)}
>
Excluir
</Button>
</CardActions>
</Card>
))}
You could use a callback function to update the state.
Try this.
const updateDepartment = async (id, department_id) => {
try {
await api.put(`volunteers/${id}/department`, {
department_id
});
setVolunteers(prevState => {
let Volunteers = prevState.map(volunteer => {
if (volunteer.id === id) {
volunteer.department_id = department_id;
return volunteer;
}
return volunteer;
});
return Volunteers;
});
toast.success("Departamento atualizado");
} catch (err) {
toast.error("Erro ao atualizar departamento");
}
};

Categories

Resources