Trying to switch between modal components using React - javascript

So I have a start page that gives options to open a login modal or sign up modal. However, once you are in the login modal I give an option so you can switch to sign up modal. However, I can't seem to get this to work. The one time I got it to work, the modal showed up in the wrong section of the screen since it was being opened in relation to the login modal and not the start page.
I am new to React so any insight would be appreciated. Should I use redux, since I can't pass props from child to parent. So that way when I return to start page I can rerender with info saying that I had clicked sign-up link on the login modal.
function LoginContent(props) {
const [ open, setOpen ] = useState(false)
const { show, closeModal } = props;
function handleSubmit(e){
e.preventDefault();
}
function handleSignUpButton(){
closeModal();
console.log(open)
setOpen(!false)
console.log(open)
}
//added so that the component doesn't get affected by parent css
//and is being rendered from the "modal-root" DOM node from the index.html file
return ReactDOM.createPortal(
<>
<div className={show ? "overlay" : "hide"} onClick={closeModal} />
<div className={show ? "modal" : "hide"}>
<button onClick={closeModal} id="close">X</button>
<div className="login_form">
<h1> Log in to Continue </h1>
<form onSubmit={handleSubmit}>
<input className="username" type='text' name='username' placeholder='Email Address' />
<input className="password" type='password' name='password' placeholder='password' />
<button className="login_button"> Sign In</button>
</form>
</div>
<div className="login_demo">
<h3 className="login_demo_pointer" type="submit">Demo Login</h3>
</div>
<hr />
<div className="login_switch">Don't have an account.
<button className="signup_link" onClick={handleSignUpButton}>Sign Up</button>
{open && <SignUpContent open={open} closeModal={closeModal} show={show} />} </div>
</div>
</>, document.getElementById("modal-root")
);
}
function Start() {
const history = useHistory();
const [showLogin, setLogin ] = useState(false);
const openModalLogin = () => setLogin(true);
const closeModalLogin = () => setLogin(false);
const [showSignUp, setShow ] = useState(false);
const openModalSignUp = () => setShow(true);
const closeModalSignUp = () => setShow(false);
return (
<div className="bodyStart">
<img src="https://i.imgur.com/5gjRSmB.gif" alt="" id="bg" />
<div className="start_logo">
<img src={require("../styling/logo.png")} alt="" onClick={() => {
history.push('/home')
history.go(0)}} className="logo" />
</div>
<div className="start">
<div className="start_heading">
<h2>Mother Nature is Calling.</h2>
<h4>Find a place to recharge and escape the day to day.</h4>
</div>
<div className="start_location">
<p>Where?</p>
<div className="start_input">
<input type="text" placeholder="anywhere" />
<ArrowForwardIcon onClick={() => {
history.push('/search')
history.go(0)}}
className="arrow" fontSize="large"/>
</div>
</div>
<div className="start_authentication">
<Button className="login"
variant="contained"
color="primary"
size="large"
onClick={() => openModalLogin()}> Login </Button>
{showLogin && <LoginContent closeModal={closeModalLogin} show={showLogin} />}
<Button className="signup"
variant="contained"
size="large"
onClick={()=> openModalSignUp()}> Sign-Up </Button>
{showSignUp && <SignUpContent closeModal={closeModalSignUp} show={showSignUp} />}
</div>
</div>
</div>
)
}

I have made similar modals with Material-UI. You can change loginOpen state and signupOpen states in modals. See codepen below
Codepen
const { useState } = React;
const { Button, Dialog, DialogTitle, DialogContent, DialogActions } = MaterialUI;
function LoginDialog(props) {
const { open, setLoginOpen, setSignupOpen } = props;
const switchSignup = (event) => {
setLoginOpen(false)
setSignupOpen(true)
}
return (
<Dialog aria-labelledby="simple-dialog-title" open={open}>
<DialogTitle id="simple-dialog-title">LOGIN</DialogTitle>
<DialogContent>If you don't have an account, press SIGNUP</DialogContent>
<DialogActions>
<Button onClick={(event) => {setLoginOpen(false)}}>CLOSE</Button>
<Button>LOGIN</Button>
<Button onClick={switchSignup}>SIGNUP</Button>
</DialogActions>
</Dialog>
);
}
function SignupDialog(props) {
const { open, setLoginOpen, setSignupOpen } = props;
const switchLogin = (event) => {
setSignupOpen(false)
setLoginOpen(true)
}
return (
<Dialog aria-labelledby="simple-dialog-title" open={open}>
<DialogTitle id="simple-dialog-title">SIGNUP</DialogTitle>
<DialogContent>If you have an account, press LOGIN</DialogContent>
<DialogActions>
<Button onClick={(event) => {setSignupOpen(false)}}>CLOSE</Button>
<Button>SIGNUP</Button>
<Button onClick={switchLogin}>LOGIN</Button>
</DialogActions>
</Dialog>
);
}
const App = () => {
const [loginOpen, setLoginOpen] = useState(false)
const [signupOpen, setSignupOpen] = useState(false)
const handleLogin = (event) => {
setLoginOpen(true)
}
const handleSignup = (event) => {
setSignupOpen(true)
}
return (
<div>
<Button variant='contained' color='primary' onClick={handleLogin} >
LOGIN
</Button>
<Button variant='outlined' color='primary' onClick={handleSignup} >
SIGNUP
</Button>
<LoginDialog open={loginOpen} setLoginOpen={setLoginOpen} setSignupOpen={setSignupOpen} />
<SignupDialog open={signupOpen} setLoginOpen={setLoginOpen} setSignupOpen={setSignupOpen} />
</div>
)
}
ReactDOM.render(<App />, document.getElementById("root"));

Related

How to display custom alert before login(navigate to other page)

Hello guys i have a project made with react, node, express, and mysql. I have a problem, in this project user can register and login. In the login page i want to display snackbar with material ui after user succeed login. But the problem is user will redirecting to ("/home) without displaying the snackbar first. I want to make the snackbar appear for like 3second and after that the user will go to the next page.
This is the code, problem occur in login function :
const Login = () => {
let navigate = useNavigate();
Axios.defaults.withCredentials = true;
const { emailLog, setEmailLog } = useContext(EmailUser);
const [ passwordLog, setPasswordLog ] = useState("");
const { loginStatus, setLoginStatus } = useContext(LoginStatus);
const [ showSuccess, setShowSuccess ] = React.useState(false);
const [ showWarning, setShowWarning ] = React.useState(false);
const handleNotShowSuccess = (event, reason) => {
if (reason === 'clickaway') {
return;
}
setShowSuccess(false);
};
const handleNotShowWarning= (event, reason) => {
if (reason === 'clickaway') {
return;
}
setShowWarning(false);
};
const [ passwordType, setPasswordType] = useState(false);
const TogglePassword = () => {
setPasswordType(!passwordType);
}
Axios.defaults.withCredentials = true;
const login = (e) => {
e.preventDefault()
Axios.post("http://localhost:3001/login" , {
email: emailLog,
password: passwordLog
}).then((response)=> {
if(response.data.message) {
setShowWarning(true)
} else{
setLoginStatus(response.data[0].email);
setShowSuccess(true);
navigate("/home");
}
})
}
useEffect(() => {
Axios.get('http://localhost:3001/login').then((response)=> {
if(response.data.loggedIn === true) {
setLoginStatus(response.data.email[0].email)
}
})
}, [])
return (
<div>
<Stack spacing={2} sx={{ width: '100%' }}>
<Snackbar open={showSuccess} autoHideDuration={6000} onClose={handleNotShowSuccess}>
<Alert onClose={handleNotShowSuccess} severity="success" sx={{ width: '100%' }}>
Logging in
</Alert>
</Snackbar>
<Snackbar open={showWarning} autoHideDuration={6000} onClose={handleNotShowWarning}>
<Alert onClose={handleNotShowWarning} severity="warning" sx={{ width: '100%' }}>
User does not exist
</Alert>
</Snackbar>
</Stack>
<div className="wrapper">
<div className="img">
<img src={userNoBg} alt="background profile"/>
</div>
<div className="login-content">
<div className='loginForm'>
<img src={user} alt="default avatar profile" />
<h2 className="title">Welcome</h2>
<div className="input-div one">
<div className="icon">
<i className="fas fa-user"><GrMail /></i>
</div>
<div className="div">
<input type="email" name="emailLogin" className="input" placeholder='Email' required
onChange={(e)=> {
setEmailLog(e.target.value)
}}/>
</div>
</div>
<div className="input-div pass">
<div onClick={TogglePassword} className="icon">
{passwordType ? <i className="fas fa-lock"><MdVisibility /></i> : <i className="fas fa-lock"><MdVisibilityOff /></i>}
</div>
<div className="div">
<input type={passwordType ? "text" : "password"} className="input" placeholder='Password' required
onChange={(e)=> {
setPasswordLog(e.target.value)
}}/>
</div>
</div>
Don't have an account ?
<button type='submit' className='btnLogin' onClick={login}>Login</button>
</div>
</div>
</div>
</div>
)
}
export default Login
Maybe I'm misunderstanding, but could you not wrap the navigate call in a setTimeout?
setLoginStatus(response.data[0].email);
setShowSuccess(true);
setTimeout(() => navigate("/home"), 3000);

Each child in a list should have a unique "key" prop console error

Please help! I don't know why I'm getting this error. I can't find what I need to change :( The needed output in browser is perfectly fine. But I am getting this error. I'm not used to list and keys on react. I have the latest versions of React and Nodejs and the packages needed
Homescreen.js:
import React, { useState, useEffect } from 'react'
import axios from 'axios';
import Room from '../components/Room';
import Loader from '../components/Loader';
import Error from '../components/Error';
function Homescreen() {
let [rooms, setrooms] = useState([]);
const [loading, setloading] = useState();
const [error, seterror] = useState();
useEffect(() => {
async function getResults() {
try {
seterror(false);
setloading(true);
const data = (await axios('/api/rooms/getallrooms')).data;
setrooms(data);
setloading(false);
} catch (e) {
seterror(true);
setloading(false);
}
}
getResults();
}, []);
return (
<div className='container'>
<div className='row justify-content-center mt-5'>
{loading ? (
<Loader />
) : rooms.length > 1 ? (
rooms.map(room => {
return <div className="col-md-9 mt-3">
<Room room={room} />
</div>;
})
) : (
<Error />
)}
</div>
</div>
)
}
export default Homescreen;
//<h1>{room.name}</h1>
and my Room.js:
import React, { useState } from "react";
import { Modal, Button, Carousel } from 'react-bootstrap'
import { First } from "react-bootstrap/esm/PageItem";
import { Link } from 'react-router-dom'
function Room({ room }) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
< div className="row bs" >
<div className="col-md-4">
<img src={room.imageurls[0]} className="smallimg" />
</div>
<div className="col-md-7">
<h1>{room.name}</h1>
<b>
{" "}
<p>Max Count : {room.maxcount}</p>
<p>Phone Number : {room.phonenumber}</p>
<p>Type : {room.type}</p>
</b>
<div style={{ float: "right" }}>
<Link to={`/book/${room._id}`}>
<button className="btn btn-primary m-5">Book Now!</button>
</Link>
<button className="btn btn-primary" onClick={handleShow}>View Details</button>
</div>
</div>
<Modal show={show} onHide={handleClose} size='lg'>
<Modal.Header>
<Modal.Title>{room.name}</Modal.Title>
</Modal.Header>
<Modal.Body>
<Carousel prevLabel='' nextLabel=''>
{room.imageurls.map(url => {
return <Carousel.Item>
<img
className="d-block w-100 bigimg"
src={url}
/>
</Carousel.Item>
})}
</Carousel>
<p>{room.description}</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={handleClose}>
Close
</Button>
</Modal.Footer>
</Modal>
</div>
);
}
export default Room;
Browser Console Error:
You have to give the first element in a map function a key:
rooms.map((room, index) => {
return (
<div key={index} className="col-md-9 mt-3">
<Room room={room} />
</div>
);
});

Load a react component on button onClick

I have the following code for a button in my ReactApp to open a new window with specific size:
<button
type="button"
style={{ height: '20px', width: '50px' }}
className="btn btn-primary"
onClick={() => {
window.open(
"",
"Popup",
"width=975, height=92, top=30"
);
}}
/>
I want to load another component when user clicks on the button. How can I achieve that? I have tried to load the component and then use it like the following, but didn't work:
import component from "./component"
<button
type="button"
style={{ height: '20px', width: '50px' }}
className="btn btn-primary"
onClick={() => {
window.open(
"{<component />}",
"Popup",
"width=975, height=92, top=30"
);
}}
/>
What would be the best way to do this?
Here is my component:
export default function component(props) {
return (
<div className="container">
<div className="cabinet"><img src={image} />
<div className="devices"> <img src={image2} />
</div>
</div>
);
}
Add a state in main page and pass it to component
const [open, setOpen] = useState[false];
const onClickHandle = () => {
setOpen(true);
};
return (
<>
<button
type="button"
style={{ height: '20px', width: '50px' }}
className="btn btn-primary"
onClick={onClickHandle}
/>
<component open={open} />
</>
);
Add a new props to control the component
export default function component(props) {
const { open } = props;
return (
<>
{open && (
<div className="container">
<div className="cabinet">
<img src={image} />
<div className="devices">
{' '}
<img src={image2} />
</div>
</div>
</div>
)}
</>
);
}
Using state hooks and conditional rendering, you can load a react component on a button onClick.
const [visible, setVisible] = React.useState(false);
function click(e) {
e.preventDefault();
setVisible(true);
}
return (
<div>
<button onClick={(event) => {
click(event)
}} />
{isVisible && (
<YourComponent />
)
}
</div>
)

Modal dialog displays from all elements of mapped array. How to select each item by ts and react js?

This code:
How to display a dialog when a button is clicked using react and typescript?
I wanna open dialog from each todos, how to make it ? I used react js and typescript. Help me to resolve this problem.
interface ListProps {
todos: INote[];
onDelete: (title: string) => void;
}
const TodoList: React.FunctionComponent<ListProps> = ({ todos, onDelete }) => {
const [showAlert, setShowAlert] = useState(false);
const [todo, setTodos] = useState(null);
How to select each item by ts?It doesn't work. What is reason? Thanks!
const handleOpenDialog = (todos: any) => {
setTodos(todos);
setShowAlert(true);
};
const handleCloseDialog = () => {
setShowAlert(false);
};
return (
<>
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
</>
);
};
export default TodoList;
just add a condition to only show the AlertDialog on selected todos
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
{showAlert && todos.title===todo?.title && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</div>
))}
</section>
or just move the AlertDialog outside the map
<section className="list list--wrapper">
{todos.map((todos) => (
<div className="item list__item" key={todos.title}>
<span className="item__title">{todos.title}</span>
<div className="item__group">
<input
className="item__completed"
type="checkbox"
checked={todos.completed}
/>
<span className="item__decs">{todos.desc}</span>
</div>
<div className="item__btn">
<button
className="item__btnd"
onClick={() => handleOpenDialog(todos)}
>
Delete
</button>
<button className="item__btne">Edit</button>
</div>
</div>
))}
{showAlert && todo && (
<AlertDialog
handleCloseDialog={handleCloseDialog}
title={todos.title}
/>
)}
</section>

Modal only shows last element of mapped array in React

Right now im trying to render an elements respective contents if the user clicks on the button. As of right now since it is creating a modal for every element once i press a the button it opens up all of the modals, showing the last one.
I looked at previous questions regarding this and some assistance would be very appreciated.
For example, once i press on a button for that project card to view more about that project, it'll only show information for the last element of the array that is being mapped.
const [modalIsOpen, setModalIsOpen] = useState(false)
return (
<>
<Grid container
direction="row"
className="Project--Items"
xs={9} >
{projects.map((project) => (
<div key={project.id}className="Project--Card">
<img src={project.image} className="Item--Main--Image"/>
<div className="Card--Text">
<p>{project.name}</p>
<p>{project.description}</p>
</div>
<button onClick={() => setModalIsOpen(true)}>Open Modal</button>
<Modal isOpen={modalIsOpen} onRequestClose={()=> setModalIsOpen(false)}>
<h2>{project.name}</h2>
<p>{project.description}</p>
<div>
<button onClick={() => setModalIsOpen(false)}>Close Modal</button>
</div>
</Modal>
</div>
))}
</Grid>
</>
);
}
const [modalIsOpen, setModalIsOpen] = useState(false)
const [selectedProject, setSelectedProject] = useState(null);
const expandModal = (project) => {
setSelectedProject(project);
setModalIsOpen(true);
}
const closeModal = () => {
setSelectedProject(null);
setModalIsOpen(true);
}
return (
<Grid container
direction="row"
className="Project--Items"
xs={9} >
{projects.map((project) => (
<div key={project.id}className="Project--Card">
<img src={project.image} className="Item--Main--Image"/>
<div className="Card--Text">
<p>{project.name}</p>
<p>{project.description}</p>
</div>
<button onClick={() => expandModal(project)}>OpenModal</button>
</div>
))}
<Modal isOpen={modalIsOpen} onRequestClose={closeModal}>
<h2>{selectedProject && selectedProject.name}</h2>
<p>{selectedProject && selectedProject.description}</p>
<div>
<button onClick={closeModal}>Close Modal</button>
</div>
</Modal>
</Grid>
);
}
The structure is a bit shaky here. You should have a hidden Modale above all your content, and simply pass it some information. It will avoid rendering countless modals on the dom.
What about something like:
function Modale ({isOpen, onClose, project}){
return isOpen && (
<>
<h2>{project.name}</h2>
<p>{project.description}</p>
<div>
<button onClick={onClose}>Close Modal</button>
</div>
</>
}
function List (){
const [modalIsOpen, setModalIsOpen] = useState(false)
const [focusProject, setFocusProject] = useState(null)
const onOpenModale = (project) => {
setModalIsOpen(true)
setFocusProject(project)
}
const onCloseModale = () => {
setModalIsOpen(false)
setFocusProject(null)
}
return (
<>
<Modale isOpen={modalIsOpen} onClose={onCloseModale} project={focusProject}/>
<Grid container direction="row" className="Project--Items" xs={9} >
{projects.map((project) => (
<div key={project.id} className="Project--Card">
<img src={project.image} className="Item--Main--Image"/>
<div className="Card--Text">
<p>{project.name}</p>
<p>{project.description}</p>
</div>
<button onClick={()=> onOpenModale(project)}>Open Modal</button>
</div>
))}
</Grid>
</>
);
}

Categories

Resources