How to open Modal from another component in hooks? - javascript

In a React project, I have the requirement of opening Modal from another component. I found suggested questions from StackOverflow but, not yet convinced. As I need to make the Modal component reusable across all components. See the code below for reference
Homepage.js
const HomePage = () => {
return (
<>
<button onClick={() => setLoginModalShow(true)}>Open Modal</button>
</>
);
};
I created the below file to export other files too, and make it useful in other components
commonLogin.js
import LoginModal from "./LoginModal";
export const setLoginModalShow = (props) => {
console.log("PROPS", props);
return <LoginModal showModal={props} />;
};
And here is the Modal component
const LoginModal = (props) => {
const [loginModalShow, setLoginModalShow] = useState(props.showModal);
console.log("PROPS in MODAL", props);
return (
<>
<Modal
show={loginModalShow}
onHide={setLoginModalShow}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">Logout</Modal.Title>
</Modal.Header>
<Modal.Body>
<h4>Are you sure to Logout?</h4>
</Modal.Body>
<Modal.Footer>
<Button onClick={() => setLoginModalShow(false)}>Cancel</Button>
</Modal.Footer>
</Modal>
</>
);
};
Please refer to the code sandbox link: https://codesandbox.io/s/nameless-cdn-70zuq.
After you visit the page click on the 'Open Modal' button

You want to control the show/hide from the parent component
const HomePage = () => {
const [showLogin, setShowLogin] = useState(false);
return (
<>
<button onClick={() => setShowLogin(true)}>Open Modal</button>
<LoginModal show={showLogin} close={() => setShowLogin(false)} />
</>
);
};
<Modal
show={props.show}
cancel={props.close}
...
Here is a working example:
https://codesandbox.io/s/nice-euclid-d0dw0

Related

How to use react-bootstrap modal in map function

I created table with some date but too better user experience I want to display it on modal. My problem is that on modal I see only first of list name and photo.
I try to do modal in component too code look more readable.
const [show, setShow] = useState(false);
const open = () => {
setShow(true)
}
return (
(..)
<thead><tr><th>miniaturka</th><th>nazwa</th><th>cena</th></tr></thead>
<tbody >
{zestawy.map((zestawy, zestaw) => {
return(<>
<tr key={zestawy.id}>
<th><Image src={`/nowemodule/${zestaw}.webp`}
width={100} height={100}/> </th>
<td>
<Button onClick={open}>
{zestawy.name} {zestawy.wielkości}</Button>
</td>
<td>{cena(zestaw).toFixed(2) }zł</td>
</tr>
<ModalPhoto
show={show} setShow={setShow}
title={zestawy.name}
content={zestawy.description}
photo={`/nowemodule/${zestaw}.webp`}
/>
</>
)
})}
</tbody>
</Table>
and mosal.jsx
function ModalPhoto({title, content, photo, show, setShow}) {
const handleClose = () => setShow(false);
return (
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>{content}</p>
</Modal.Body>
<Image src={photo} width={100} height={100}/>
<Modal.Footer>
<Button variant="secondary">Close</Button>
</Modal.Footer>
</Modal>
);
}
export default ModalPhoto;
The big problem is that you use a unique show state for all indexes of zestawy array. So when you set show = true, all iterations of ModalPhoto open. You only see the last one in HTML.
To fix this, you can set a showByZestawy object that contains zestawy id as key and a boolean for open or not in value :
const [showByZestawy, setShowByZestawy] = useState({});
const handleOpen = (zestawy) => {
setShowByZestawy({...showByZestawy, [zestawy.id]: true })
}
const handleClose = (zestawy) => {
const nextShowByZestawy = {...showByZestawy};
delete nextShowByZestawy[zestawy.id];
setShowByZestawy(nextShowByZestawy)
}
//...
<ModalPhoto show={showByZestawy[zestawy.id]} onHide={handleClose} />
You set key={zestawy.id} on <tr> in your map but this is an error because the first child of your map is a Fragment that contains the Modal.
So you should do :
<Fragment key={zestawy.id}> // nameless Fragments like <> cannot have a key props
<tr>{...}</tr>
<ModalPhoto/>
</Fragment>

how to show react bootstrap modal inside the function

I am using react bootstrap modal popup. when user submit form, I need to modal show popup,
This is my modal (SaveConfirmationModal )
import { Button, Modal} from "react-bootstrap";
function SaveConfirmationModal(props) {
return (
<div>
<Modal {...props} size="lg">
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
do u want to save ?
</Modal.Title>
</Modal.Header>
<Modal.Body>
<button>YEs </button>
<button>No</button>
</Modal.Body>
</Modal>
</div>
);
}
export default SaveConfirmationModal;
this is my invoice page save function. I imported my modal in to the invoice page.
import SaveConfirmationModal from "components/createinvoice/SaveConfirmationModal";
const loadPopup= (data) => {
showmodal;
if(yes){
saveForm();
}
else{
close modal
}
}
const saveForm= (data){
my save function
}
my save button in the invoice page
<button onClick={loadPopup}> Save </button>
This is the very little sample to demonstrate my issue. If you can please help me to show this confirmation box. thanks
In the component where you use your SaveConfirmationModal, you can use state Hooks, like this:
import React, {useState} from 'react';
...
const [showModal, setShowModal] = useState(false);
const loadPopup = () => {
setShowModal(true);
};
And you need to change the state to false when the modal is dismissed:
<SaveConfirmationModal show={showModal} onHide={() => setShowModal(false)}
You can call a function from a prop on button click like below.
import { Button, Modal} from "react-bootstrap";
function SaveConfirmationModal(props) {
return (
<div>
<Modal {...props} size="lg">
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
do u want to save ?
</Modal.Title>
</Modal.Header>
<Modal.Body>
<button onClick={props.onYes}>YEs </button>
<button onClick={props.onNo}>No</button>
</Modal.Body>
</Modal>
</div>
);
}
export default SaveConfirmationModal;
Then in your invoice page create a state for showing a modal and pass the props like below.
import { useState } from 'react'
import SaveConfirmationModal from "components/createinvoice/SaveConfirmationModal";
const InvoicePage = () => {
const [showModal, setShowModal] = useState(false);
const saveForm = (data) {
// my save function
}
return (
<>
<button onClick={() => setShowModal(true)}> Save </button>
<SaveConfirmationModal show={showModal} onYes={() => saveForm(data)} onNow={() => setShowModal(false)} />
</>
)
}

Trying to open and close a Modal in different components

I am using a Material UI Dialog Form between two components. In the parent component that is Productos.js, I open the component and the child component EditarProductos.js receives the open state from its parent.
I am having trouble because I can only open the dialog once and then I can't open it again.
Below is the code from Productos.js (parent component)
//Code...
import EditarProductos from './EditarProductos';
//Code...
function Productos(props) {
const [open, setOpen] = React.useState(false);
//Code...
function editar(producto){
//Code...
setOpen(true);
}
//Code...
return (
<div>
{id && nombre && precioCompra && precioVenta && cantidad && open &&
<EditarProductos productoEdit={id} productoNombre={nombre} productoPrecioCompra ={precioCompra}
productoPrecioVenta = {precioVenta} productoCantidad = {cantidad} isOpen = {open}/>
}
<br />
//Code...
<br />
<Table className={classes.table}>
<TableHead>
<TableRow>
//Code...
</TableRow>
</TableHead>
<TableBody>
{productos.map((producto) =>
<TableRow className="data-row">
<StyledTableCell>{producto.id
}</StyledTableCell>
<StyledTableCell>{producto.nombre}</StyledTableCell>
<StyledTableCell>{producto.precio_compra}</StyledTableCell>
<StyledTableCell>{producto.precio_venta}</StyledTableCell>
<StyledTableCell>{producto.cantidad}</StyledTableCell>
<StyledTableCell>{producto.categorias_id}</StyledTableCell>
<StyledTableCell>
<Button variant="outlined" onClick={() => editar(producto)}>
<EditIcon />
</Button>
<Button variant="outlined" onClick={() => eliminar(producto)} ><DeleteIcon /> </Button>
</StyledTableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
);
}
export default Productos;
Below is EditarProdutos.js (child component)
import {Button, TextField, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from '#material-ui/core';
function EditarProductos(props){
var abierto = props.isOpen;
const [open, setOpen] = React.useState(abierto);
const handleClose = () => {
abierto = false;
};
//Code...
return (
<div>
<Dialog open={open} onClose={handleClose} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">Editar</DialogTitle>
//Code...
<Button onClick={handleClose} color="primary">
Cancelar
</Button>
//Code...
</DialogActions>
</Dialog>
</div>
);
}
export default EditarProductos;
Thank you so much!
The problem with your code is the internal state that EditarProductos component holds.
The idiomatic and easy solution for this would be, that you pass down the isOpen flag and the handleClose method from the parent, the EditarProductos wouldn't have an internal state, just use the props:
function EditarProductos(props){
return (
<div>
<Dialog open={props.isOpen} onClose={props.handleClose}>
<DialogTitle>Editar</DialogTitle>
<DialogActions>
<Button onClick={props.handleClose} color="primary">
Cancelar
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default EditarProductos;
And in your Productos component you need to define a handleClose method as well (with setOpen(false)) and you need to pass down that too with isOpen

React direct to a "thank you" page after submitting a form

I made a simple form and after I hit the submit button of the form, I want it to automatically redirect to a "thankyou" page I have set up. Can anyone help me solve this.
Currently, this is how I think it might work, but it keeps popping up errors.
Thank you very much.
const handleSubmit = event => {
alert("Thank you for registering ");
event.preventDefault();
event.stopPropagation();
handleClose();
redirectToPage();
};
const redirectToPage = () => redirect(true);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
if (redirect) {
return <Link to="/thankyou" />
}
return (
<>
<div className="StudentForm">
<Modal show={show} onHide={handleClose} animation={true}>
<Modal.Header closeButton>
<Modal.Title>Student Information</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<Row>
<Col>
<Form.Group controlId="std_name">
<Form.Control required type="text" id="std_name" name="std_name"
placeholder="Student Name" />
</Form.Group>
</Col>
</Row>
<Button variant="secondary" onClick={event => window.location.href='/home'}>Cancel</Button>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</Modal.Body>
</Modal>
</div>
</>
);
you wanna use Redirect from react-router-dom and i would suggest using useState hook to maintain a state variable which would determine when to redirect and in redirectToPage you would just set it to true
import React,{Fragment,useState} from "react";
import { Redirect } from "react-router-dom";
const [redirect, setRedirect] = useState(false);
const redirectToPage = () => setRedirect(true) ;
return (
{redirect && (
<Fragment>
<Redirect to='/thankyou' />
</Fragment>
)}
<div className="StudentForm">
// Rest of Code
);

TypeScript React Invalid Hook Call Error ("Error: Invalid hook call.")

I tried asking a question earlier but realized it was a bit all over the place so no one could really understand it properly. So here's a more clarified attempt.
I am currently working on a CRUD web project that allows the user to edit, view and create more users which is then displayed on a table.
I have been using react-bootstrap's components so decided to use the Modal component provided. (https://react-bootstrap.netlify.com/components/modal/#modals-live)
It was successfully working without any errors when I just called and used the component within App.tsx as follows:
const App: React.FC = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="App">
<NavBar/>
<Jumbotron fluid className="userJumbotron">
<Container>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>Add new user</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body><NewUserForm/></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
But I think realized that I'll need more than 1 modal component for my program and decided to make the modal component section into my own component that I can reuse as many times as I want and customize as I want within the App.tsx
So I decided to make a NewModal.tsx component that has the bootstrap modal component and button pre-made meaning I'd only have to call one line () rather than a whole chunk. the NewModal.tsx code is as follows:
export default class NewModal extends Component<any> {
constructor (props: any){
super(props)
this.state={
show:false
}
}
render() {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>Add new user</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body><NewUserForm/></Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
}
I am getting the following error from this code.
What is the cause for this error?
You can't use hooks inside class components, you need to change it to be a function based component, which can look something like this:
const NewModal = () => {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div>
<h1>Manage Users</h1>
<Button variant="outline-dark" onClick={handleShow}>
Add new user
</Button>
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Add User</Modal.Title>
</Modal.Header>
<Modal.Body>
<NewUserForm />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
<Button variant="primary" onClick={handleClose}>
Save Changes
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default NewModal;

Categories