Dispatch a Redux action from a modal in React - javascript

I'm a begginer in React and I'm trying to find a way to dispatch a redux action from a modal.
I have a list of products and a button 'add to bag' under each image. When we click on the button 'add to bag', I want a modal to appear that ask for a confirmation. The action need to be dispatched when the user click on the confirm button inside de modal window.
The action need to grab the item object.
All is working fine ...but I'm not able to pass the item into the action when I want to launch the action from the modal.
So, my problem is not the Redux side but the modal part ( I use React-bootstrap for the modal).
I have this error message : 'item' is not defined
I'm not sure I understand exactly why this does'nt work and I failed to find a solution. I tried so many things but it's just not working.
Is there a simple way to add/pass the item data into the modal easily ?
Help would be very appreciated ! :)
Thanks a lot !!!
Here are part of my files :
Product.js
import { Modal } from "react-bootstrap";
function Products(props) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<CardGroup>
{_.map(props.data, (item) => (
<Col>
...Some code here
<div>
<div>
{item.name.map((item) => (
<ul key={item}>{item}</ul>
))}
</div>
<div>
<button onClick={() => {handleShow()}}>
Add to bag
</button>
</div>
</div>
</Col>
))}
</CardGroup>
<Modal show={show} onHide={handleClose}>
<Modal.Body>Please confirm you want to add this product</Modal.Body>
<Modal.Footer>
<button
onClick={props.addProductToBasket(item)}
>
Confirm
</button>
<button onClick={handleClose}>Cancel</button>
</Modal.Footer>
</Modal>
</div>
);
}
const mapStateToProps = (state) => {
return { code here ...};
};
const mapDispatchToProps = (dispatch) => {
return {
addProductToBasket: (id) => dispatch(addProductToBasket(id)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Products);
Here is the part of my Product.js file im my Store/actions folder
export const addProductToBasket = (id) => {
return {
type: ADD_PRODUCTD_TO_BASKET,
payload: id,
};
};

Since you only show the modal if an item was clicked, store the item or null instead of Boolean in the state, and open and close the modal accordingly:
function Products(props) {
const [selectedItem, setShowItem] = useState(null); // item for open model or null for closed
const handleClose = () => setShowItem(null);
const handleShow = item => setShowItem(item);
return (
<div>
<CardGroup>
{_.map(props.data, (item) => (
<Col>
...Some code here
<div>
<div>
{item.name.map((item) => (
<ul key={item}>{item}</ul>
))}
</div>
<div>
<button onClick={() => handleShow(item)}>
Add to bag
</button>
</div>
</div>
</Col>
))}
</CardGroup>
<Modal show={selectedItem !== null} onHide={handleClose}>
<Modal.Body>Please confirm you want to add this product</Modal.Body>
<Modal.Footer>
<button
onClick={() => props.addProductToBasket(selectedItem)}
>
Confirm
</button>
<button onClick={handleClose}>Cancel</button>
</Modal.Footer>
</Modal>
</div>
);
}
Not related, but it will make your life easier - use the object form of mapDispatchToProps that will save you the need to wrap with dispatch manually:
const mapDispatchToProps = {
addProductToBasket
};

Related

How to show modal based on what was clicked on the card [duplicate]

This question already has an answer here:
How do I use React Modal with a map function?
(1 answer)
Closed 6 months ago.
How can I show the modal only based on card was clicked?
I have this dynamic card. Once I clicked on a specific card, how can it show the modal with a title of the data's id? As of now, once I click on a card, it will only show the last id.
Main app
const [isOpen, setisOpen] = useState(false);
const handleOpen = (id) => {
console.log(id);
setOpen(true)
};
const handleClose = () => {
setisOpen(false);
};
....
{data.map((i) => (
<Grid
key={data.indexOf(i)}
>
<CardonClick={(e) => handleOpen(i.id)}>
<CardHeader title={i.title} />
</Card>
<Modal isOpen={isOpen} handleClose={handleClose} title={i.id}/> <-- display of the id here
</Grid>
))}
Modal reusable component
import {
Dialog,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Button,
DialogActions,
} from "#mui/material";
const Modal = ({ title, subtitle, children, isOpen, handleClose }) => {
return (
<Dialog open={isOpen} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{subtitle}</DialogContentText>
<Divider />
{children}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="error">
No
</Button>
</DialogActions>
</Dialog>
);
};
export default Modal;
You can use useState hook and store the relevant data (id in your case) in handleOpen function. Then you can use that data in your modal. example:
const [isOpen, setisOpen] = useState(false);
const [currentModalId,setCurrentModalId]=useState("");
const handleOpen = (id) => {
console.log(id);
setCurrentModalId(id);
setOpen(true)
};
const handleClose = () => {
setisOpen(false);
};
And then use it:
...<Modal> <div> {id} </Modal>

Passing a single boolean useState between two pages using Material UI web components

I've been trying to pass in a boolean useState between two pages, using the best practice shown here: How to call useState from another Page?
My main objective is to show a success alert on a different page, once a form is successfully submitted on "./demo"
Demo.js holds a dialog with a submit button that sets the setSuccess to true.
import Alert from "./alert";
export default function AlertDialog() {
const [success, setSuccess] = React.useState(false); // <- Hides and Shows the Alert Message
const handleSubmit = () => {
return (
<Alert
setSuccess={() => {
setSuccess(true); // <- How I am trying to setSuccess to true.
}}
/>
);
};
return (
<DialogActions>
<Button
onClick={handleSubmit}
color="primary"
autoFocus
component={RouterLink}
to={"/"}
>
Submit
</Button>
</DialogActions>
);
Alert.js has an alert message that appears once success is set to true.
export default function Alerts(props) {
// const [open, setOpen] = React.useState(false);
const { success, setSuccess } = props;
return (
<div>
<Collapse in={success}>
<Alert
action={
<IconButton
aria-label="close"
color="inherit"
size="small"
onClick={() => {
setSuccess(false);
}}
>
<CloseIcon fontSize="inherit" />
</IconButton>
}
>
Form Successfully Submitted
</Alert>
</Collapse>
<Button
disabled={success}
variant="outlined"
component={RouterLink}
to={"/demo"}
>
Go Back to Submit Form
</Button>
</div>
)
;
}
Could someone explain how I can have the success alert appear after submission? If you wish to take a deeper dive, please visit here https://codesandbox.io/s/alert-test-qhkbg?file=/alert.js
I think what you're looking for here is passing state through React Router. Right now your alert isn't updating because your URL is changing when the submit button is pressed.
Check out this sandbox. I'm passing a message and a property to get the alert to render within the / route with different logic.
This is the key snippet:
<Button
color="primary"
autoFocus
component={RouterLink}
to={{
pathname: "/",
state: { message: "hello, i am a state message", open: true }
}}
>
Submit
</Button>
And then in your alert component at the url / you can:
const [open, setOpen] = React.useState(false);
React.useEffect(() => {
props.location.state?.open ? setOpen(true) : setOpen(false);
}, [props]);
// This message is what is being passed. Could be anything.
console.log(props.location.state);

How to stop useEffect from loading my modal multiple times?

I have a function based React view which has a modal that loads based on the presence of an item in the local storage:
function myView() {
...
function HelpModal() {
const [show, setShow] = useState(false);
useEffect(()=>{
let pop_status = localStorage.getItem('this-page-modal');
if(!pop_status){
setShow(true);
}
},[])
if(!setShow) return null;
const handleClose = () => {
localStorage.setItem('this-page-modal', '1');
setShow(false);
};
const handleShow = () => setShow(true);
return (
<>
<Button variant="primary" onClick={handleShow}>
Button text
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Body>
<p>Hey! This is a modal.</p>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
return (
<div>
<Container style={{marginTop: "1em"}}>
<Row>
<Col><h1>My View</h1></Col>
<Col><span style={{float: "right"}}> <HelpModal /></span></Col>
</Row>
</Container>
</div>
);
}
The problem: When I load the page, the modal flickers and loads 2 or 3 times.
How do I make it load only once? Any suggestions will be appreciated.
For the general case, you can use useLayoutEffect to make the update occur quicker, near instantaneously, instead of a bit after a repaint with useEffect.
Also, the if(!setShow) return null; line is completely superfluous because the useState setter function will always be a function, which is truthy.
useLayoutEffect(() => {
const pop_status = localStorage.getItem('this-page-modal');
if (!pop_status) {
setShow(true);
}
}, []);
But since the function inside the effect is completely synchronous, it'd make more sense to use the storage value as the initial value for the show state, instead of calling the state setter; you an remove the effect entirely.
const [show, setShow] = useState(!localStorage.getItem('this-page-modal'));
const handleClose = () => {
localStorage.setItem('this-page-modal', '1');
setShow(false);
};
const handleShow = () => setShow(true);
return (
// ...
Your setup could be causing re-renders based upon code not represented here so it's difficult to diagnose the exact issue. One thing to try might be to put a console.log statement in various function scopes to see how many times it's being re-rendered on load. Aside from that, (and concurring with previous answer) I think you might be able to simplify your code down a bit and get the same result by having the conditional render be straight from localStorage. Maybe something like:
function HelpModal() {
const [show, setShow] = useState(localStorage.getItem("this-page-modal"));
const handleClose = () => {
setShow(false);
localStorage.setItem("this-page-modal", false);
};
const handleShow = () => {
setShow(true);
localStorage.setItem("this-page-modal", true);
};
return (
<>
<Button variant="primary" onClick={handleShow}>
Button text
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Body>
<p>Hey! This is a modal.</p>
</Modal.Body>
<Modal.Footer>
<Button variant="primary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</>
);
}
function myView() {
return (
<>
<HelpModal />
</>
);
}

Rendering Modal Popup

My goal is to launch a modal pop-up during the event a button is clicked. I've been having issues making the Axios API call to the function that contains the modal. The process of the API call has been listed below:
First: This is the button where the Axios post request is made (addToCart) (works fine)
<button className="button is-primary" onClick={addToCart}>
ADD TO CART
</button>
Second: This is the Axios post request that makes a call to the modal pop-up (Example()) (works fine)
const addToCart = () => {
Axios.post(
//standard backend API call is made here
)
.then((res) => {
console.log("post called")
Example(); //I want to launch my modal when this state is reached
})
.catch((err) => {
if (err.response && err.response.status === 401) {
setIsLoggedIn(false);
}
});
Third: This is the modal that I'd like to popup but have been having issues rendering the function (Issue)
https://react-bootstrap.github.io/components/modal/
function Example() {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<>
<Button variant="primary" onClick={handleShow}>
Launch demo modal
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</>
);
}
render(<Example />);
I receive the following error:
Line 38:3: 'render' is not defined no-undef
How can I properly render my modal pop-up?
Instead of having a show state in your Example component, receive this data in a prop. In addition, you'll also want to pass some callbacks to the component to be able to react to certain events (e.g. clicking "Close" or "Save Changes").
I'd do the following:
const Example = ({ show, onClose, onSave }) => (
<Modal show={show} onHide={onClose}>
<Modal.Header closeButton>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>Woohoo, you're reading this text in a modal!</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onClose}>
Close
</Button>
<Button variant="primary" onClick={onSave}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
)
This way you could always render the Example component in some parent component and show it conditionally, like so:
const SomeParentComponent = () => {
const [showExample, setShowExample] = useState(false)
const addToCart = () => {
Axios.post("http://example.com/api/add-to-cart")
.then((res) => {
console.log("post called")
setShowExample(true)
})
.catch((err) => {
if (err.response && err.response.status === 401) {
setIsLoggedIn(false);
}
})
}
return (
<>
<button className="button is-primary" onClick={addToCart}>
ADD TO CART
</button>
<Example
show={showExample}
onClose={() => setShowExample(false)}
onSave={() => setShowExample(false)}
/>
</>
)
}

React (Nextjs): render table row to boostrap modal onclick

I have the following Nextjs page using React bootstrap modal and a MUI datatables component:
const Start = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const options = {
onTableInit: rowData,
onRowClick: rowData => Testing(rowData),
}
return (
<Layout>
<Modal show={show} onHide={handleClose}>
<Modal.Header>
<Modal.Title>Modal heading</Modal.Title>
</Modal.Header>
<Modal.Body>
Modal content
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
<MUIDataTable
title={"Datatable with modal onRowClick"}
data={data}
columns={columns}
options={options}
responsive="scrollFullHeight"
/>
</Layout>
)
}
export default Start;
The bootsrap modal works fine and opens onRowClick, I am also able to console log the rowData in a function when clicking a certain row:
function Testing(rowData) {
console.log(rowData)
);
I would like to achieve passing this rowData to the bootstrap modal accordingly when clicked on certain row.
I know I should be using the mapping, since it's an JSON object
{rowData.map(item => {
return <li key={item}>{item}</li>
}
)}
But how to integrate this into my current system? I get the error on rowData is not a function.

Categories

Resources