how to open modal2 from a button of modal1 in react - javascript

I have two modals one is Login modal and another is Signup modal now I want to open signup modal by clicking on a button which is present in login modal, I tried many different ways but not able to achieve it, also I tried to make a signupModalSwitch with useReducer and by making different function calls but it was saying against hooks rule, I am very new to react not able to figure out how to do it. Thanks in Advance :)
modal1 ->
function LoginModal() {
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Button
onClick={handleOpen}
variant="outlined"
className={classNames(
classes.textNeonGreen,
classes.outlinedNeonGreen,
classes.navButton
)}
classes={{ disabled: classes.disabled }}
>
<Typography noWrap>Login</Typography>
</Button>
<Modal
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<LoginCard LoginClose={handleClose} />
</Fade>
</Modal>
</div>
);
}
function LoginCard({ LoginClose }) {
return (
<Card className={classes.modalCard}>
<span>
<button
onClick={LoginClose}
type="button"
className="close px-2 pt-2"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</span>
<CardContent className={classes.modalCardContent}>
<Typography variant="h4" className={classes.modalHeading}>
<b>Login</b>
</Typography>
<Account>
<Login />
</Account>
<div>
<Grid
container
direction="column"
justify="center"
alignItems="center"
>
<p className="mb-2 mt-4 text-center">
Don't have Account?
</p>
//###### BY CLICKING THIS BUTTON I WANT TO OPEN MY SIGNUP MODAL #######//
<button
className="btn btn-outline-success btn-block btn-md"
onClick={() => {
LoginClose();
signupModalSwitch(null,{type:'open'})
}
}
>
Signup
</button>
</Grid>
</div>
</CardContent>
</Card>
);
}
MODAL2 ->
function signupModalSwitch(state, action){
switch(action.type){
case 'open':
return { open: true }
case 'close':
return { open: false }
default:
console.log(action);
}
}
function SignupModal() {
const classes = useStyles();
const [state, dispatch] = useReducer(signupModalSwitch, { open: false })
function handleOpen() {
dispatch({type: "open"});
}
function handleClose() {
dispatch({type: "close"});
};
return (
<div>
<Button
onClick={handleOpen}
variant="outlined"
className={classNames(
classes.textNeonGreen,
classes.outlinedNeonGreen,
classes.navButton
)}
classes={{ disabled: classes.disabled }}
>
<Typography noWrap>Signup</Typography>
</Button>
<Modal
className={classes.modal}
open={state.open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={state.open}>
<SignupCard signupClose={handleClose} />
</Fade>
</Modal>
</div>
);
}
function SignupCard({ signupClose }) {
return (
<Card className={classes.modalCard}>
<span>
<button
onClick={signupClose}
type="button"
className="close px-2 pt-2"
aria-label="Close"
>
<span aria-hidden="true">×</span>
</button>
</span>
<CardContent className={classes.modalCardContent}>
<Typography variant="h4" className={classes.modalHeading}>
<b>Signup</b>
</Typography>
<Signup />
</CardContent>
</Card>
);
}

A better way to do this would be to create a container and move all the logic to the container. Your login and sign up modals should be dumb components, i.e, do not maintain the state in these components.
In your container, keep two states, openLoginModal and openSignupModal.
const [openLoginModal, setOpenLoginModal] = useState(false)
const [openSignupModal, setOpenSignupModal] = useState(false)
You can pass these as props to your login and signup components. When you click the button in the login modal, you will need one more function that closes login modal and opens sign up modal. You can pass this as prop as well.
const onSignupButtonClick = () => {
setOpenLoginModal(false)
setOpenSignupModal(true)
}
You can conditionally render login and signup modal based on the state values.

Related

How to use multiple material ui dialog with React?

I want to use two dialog in sign up page and login page.
the implement that I want to do are signup screen showing up when click to sign up button on the Top page, and login screen showing up when click to login Button on signup page.
I wrote open state on App.js.
but the trouble is when it's written in App.js, two of modal open..
Does anyone know how to settle it?
App.js
const App = () => {
const [open, setOpen] = useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
<Top handleClickOpen={handleClickOpen}/>
<SignupModal open={open} handleClose={handleClose} />
<LoginModal open={open} handleClose={handleClose} />
</div>
)
Top.js
const Top = (props) => {
const classes = useStyles();
return (
<React.Fragment>
<div style={style}>
<Button variant="outlined" className={classes.loginButton} onClick={props.handleClickOpen}>
Login
</Button>
</div>
<h1>Toppage</h1>
</React.Fragment>
);
}
SignupModal.js
const SignupModal = (props) => {
return (
<div>
<Dialog
open={props.open}
onClose={props.handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title" className={classes.title}>Sign up</DialogTitle>
<DialogContent className={classes.content}>
<div className={classes.text}>
<TextField id="standard-basic" label=“name” fullWidth/>
<TextField id="standard-basic" label=“email” fullWidth/>
<TextField id="standard-basic" label=“password” fullWidth/>
<TextField id="standard-basic" label=“pass”word fullWidth/>
</div>
</DialogContent>
<p className={classes.toLogin}>to Login</p>
<DialogActions>
<Button onClick={props.handleClose} className={classes.signUpButton}>
Send
</Button>
</DialogActions>
</Dialog>
</div>
);
}
LoginModal.ls
const LoginModal = (props) => {
return (
<div>
<Dialog
open={props.open}
onClose={props.handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title" className={classes.title}>Login</DialogTitle>
<DialogContent className={classes.content}>
<div className={classes.text}>
<TextField id="standard-basic" label=“name” fullWidth/>
<TextField id="standard-basic" label=“pass”word fullWidth}/>
</div>
</DialogContent>
<DialogActions>
<Button onClick={props.handleClose} className={classes.signUpButton}>
Login
</Button>
</DialogActions>
</Dialog>
</div>
);
}
export default LoginModal
You are sharing the state on both of your modals that's why.
The solution is simple, have 2 states; one that will determine if SignupModal is opened or not and another one for LoginModal.
const App = () => {
const [openLogin, setOpenLogin] = useState(false);
const [openSignup, setOpenSignup] = useState(false);
return (
<div>
<Top
onClickLogin={() => setOpenLogin(true)}
onClickSignup={() => setOpenSignup(true)}
/>
<SignupModal open={openLogin} handleClose={() => setOpenLogin(false)} />
<LoginModal open={openSignup} handleClose={() => setOpenSignup(false)} />
</div>
);
};
const Top = (props) => {
return (
<React.Fragment>
<div>
<Button
variant="outlined"
className={classes.loginButton}
onClick={props.onClickLogin}
>
Login
</Button>
<Button
variant="outlined"
className={classes.loginButton}
onClick={props.onClickSignup}
>
Signup
</Button>
</div>
<h1>Toppage</h1>
</React.Fragment>
);
};

No unused Variables

In my useState hook, I am importing context; thus, setUser is going unused and giving me and Eslinting warning. I am unsure of how to stifle this warning and have run out of ideas in order to do so. If anyone has a suggestion or best practices in React for stifling this warning I would greatly appreciate it. the code is as follows:
import React, { useContext } from 'react'
import { Link } from 'react-router-dom'
// Material UI
import Button from '#material-ui/core/Button'
import Grid from '#material-ui/core/Grid'
import Container from '#material-ui/core/Container'
import User from './User'
// context
import { ProfileContext } from '../contexts/ProfileContext'
const Header = ({ isAuth, logout }) => {
const [user, setUser] = useContext(ProfileContext)
return (
<Container maxWidth="lg" style={{ padding: 10 }}>
<Grid container justify="space-between">
<Grid item xs={2}>
<Button color="inherit" component={Link} to="/">
Jobtracker
</Button>
</Grid>
<Grid item xs={10} container justify="flex-end">
<div>
{isAuth ? (
<>
{user && user.user.admin && (
<Button color="inherit" component={Link} to="/admin">
Admin
</Button>
)}
<Button color="inherit" component={Link} to="/profile">
Profile
</Button>
<Button color="inherit" component={Link} to="/dashboard">
Dashboard
</Button>
<Button color="inherit" onClick={logout}>
Logout
</Button>
</>
) : (
<>
<Button color="inherit" component={Link} to="/login">
Login
</Button>
<Button color="inherit" component={Link} to="/signup">
SignUp
</Button>
</>
)}
</div>
</Grid>
</Grid>
</Container>
)
}
export default Header
You don't have to destructure it when you don't need it:
const [user] = useContext(ProfileContext)
In addition, if you just need to setUser, you can skip items while destructuring by using a comma without an variable or constant name:
const [, setUser] = useContext(ProfileContext)
If you are not using setUser just remove it from destructuring

Copied code for dialog in material-ui from docs, but isn't working, what am I doing wrong?

I copied code for material-ui dialog feature for react, but couldn't figure out why this isn't working at all. Clicking the contact button doesn't even cause it to call the handleClickOpen method.
The contact button is the one that's supposed to open the dialog box, all the dialog code is copied from the docs of material-ui so I'm not sure how this couldn't be working.
export default function Banner() {
const [open, setOpen] = React.useState(false);
function handleClickOpen() {
setOpen(true);
}
function handleClose() {
setOpen(false);
}
const classes = useStyles();
return (
<Container maxWidth="lg">
<div className={classes.root}>
<Grid container spacing={7}>
<Grid item lg={6} xs={12}>
<div className={classes.title}>
<Title content="Freightage Solutions" />
<br />
<SubTitle content="A lean, modern, and efficient shipping brokerage." />
<div className={classes.buttons}>
<Button ClassName={classes.button} content="Get Started" color='white' />
<Button ClassName={classes.button} content="Contact Us" color='blue' onClick = {handleClickOpen} />
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Use Google's location service?"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Let Google help apps determine location. This means sending anonymous location data to
Google, even when no apps are running.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Disagree
</Button>
<Button onClick={handleClose} color="primary" autoFocus>
Agree
</Button>
</DialogActions>
</Dialog>
</div>
</div>
</Grid>
<Grid item lg={6} xs={12}>
<img src={Image} className={classes.image} />
</Grid>
</Grid>
</div>
</Container>
);
}
EDIT: Here is the custom button component I'm using
import React from 'react';
import Typography from '#material-ui/core/Typography';
import { styled } from '#material-ui/styles';
import createBreakpoints from "#material-ui/core/styles/createBreakpoints";
import Button from "#material-ui/core/Button"
const breakpoints = createBreakpoints({});
const CustomButton = styled(Button)({
border: '2px solid #FFFFFF',
borderRadius: 80,
height: 48,
padding: '0 20px',
textTransform: 'none',
marginBottom: '20px',
marginRight: '30px',
marginLeft: '30px',
[breakpoints.up("lg")]: {
marginLeft: '0px',
},
});
const BlueButton = styled(CustomButton)({
background: '#0071F7',
color: 'white',
'&:hover': {
background: 'white',
color: '#0071F7',
},
});
const WhiteButton = styled(CustomButton)({
background: 'white',
color: '#0071F7',
'&:hover': {
background: '#0071F7',
color: 'white',
}
});
const ButtonType = styled(Typography)({
fontFamily: 'Ubuntu',
fontWeight: 450,
});
export default class Title extends React.Component {
render (){
if(this.props.color == 'white'){
return (
<WhiteButton gutterBottom>
<ButtonType>
{this.props.content}
</ButtonType>
</WhiteButton>
)
} else{
return(
<BlueButton gutterBottom>
<ButtonType>
{this.props.content}
</ButtonType>
</BlueButton>
)
}
}
}
It would be a good idea to use the onClick-prop you provided with to your CustomButton and set it on your button.
export default class Title extends React.Component {
render () {
if(this.props.color == 'white'){
return (
<WhiteButton onClick={this.props.onClick} gutterBottom>
<ButtonType>
{this.props.content}
</ButtonType>
</WhiteButton>
)
} else{
return(
<BlueButton onClick={this.props.onClick} gutterBottom>
<ButtonType>
{this.props.content}
</ButtonType>
</BlueButton>
)
}
}
}
As per the API Doc, there is no props called content for Button instead use children like,
<Button className={classes.button} children="Get Started" style={{color:'white'}} />
<Button className={classes.button} children="Contact Us" style={{color:'blue'}} onClick = {handleClickOpen} />
Update
You are using Button name to your custom component and material-ui also have the component with same name. As you are using both in same place there is a conflict and not a error from material-ui which one to use and your functionality is not working. This is probably the problem.
Try to change your custom button component name and check if it works.
Update 2
if(this.props.color == 'white'){
return (
<WhiteButton gutterBottom>
<ButtonType>
<Button onClick={this.props.onClick}>{this.props.content}</Button> //You forgot to use Button here
</ButtonType>
</WhiteButton>
)
} else{
return(
<BlueButton gutterBottom>
<ButtonType>
<Button onClick={this.props.onClick}>{this.props.content}</Button>
</ButtonType>
</BlueButton>
)
}
you should use proper material-ui Button API(https://material-ui.com/api/button/)
<Button children="Get Started" style={{color:'white'}} />
<Button children="Contact Us" style={{color:'blue'}} onClick = {handleClickOpen} />
check this: https://codesandbox.io/s/3fl8r

How can I pass data from one card, in a list of cards, to a dialog?

I want to add a confirmation step for my CRUD application using a Material-UI dialog, but I don't seem to be able to pass any data to my dialog.
I have a list/grid of cards that I've created using .map(), each of which contains data I pulled from a MongoDB document. I'm able to delete the documents/cards from my app with just a button, but now I'd like to add a confirmation step to the deletion process, using a Material-UI dialog. To do that, I need to pass data from the cards to the dialog (if that's the correct phrasing for what I'm trying to do). I can't figure out how to do that.
I've tried passing the data using this.props.match.params.id, this.id, this._id, this.oneSavedArticle.id, and this.props.oneSavedArticle.id, but they all have either returned an error or undefined.
Here is my delete function:
handleDelete = id => {
console.log(id);
API.deleteArticle(this)
.then(res => console.log(res.data))
.catch(err => console.log(err));
// this.props.history.push("/saved");
};
Here is my dialog:
<div>
<Button
color="secondary"
variant="outlined"
onClick={this.handleClickOpen}
>
DELETE
</Button>
<Dialog
open={this.state.open}
TransitionComponent={Transition}
keepMounted
onClose={this.handleClose}
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle>
{"Delete this article?"}
</DialogTitle>
<Divider variant="middle" />
<DialogActions>
<Button onClick={this.handleClose} color="secondary">
NO
</Button>
<Button
onClick={() => this.handleDelete(this.props)}
color="primary"
>
YES
</Button>
</DialogActions>
</Dialog>
</div>
Here is my API route:
deleteArticle: function(id) {
return axios.delete("/api/articles/" + id);
}
Here is where and how I've implemented the dialog into my list of cards:
{this.state.savedArticles.length ? (
<Grid>
{this.state.savedArticles.map((oneSavedArticle, i) => (
<Card style={savedArticleCard} key={i}>
<Typography variant="h5">{oneSavedArticle.headline}</Typography>
<Divider variant="middle" />
<Typography>{oneSavedArticle.snippet}</Typography>
<a href={oneSavedArticle.web_url} style={linkStyles}>
<Button style={buttonStyles}>READ IT</Button>
</a>
<DeleteDialogue {...this.props} />
</Card>
))}
</Grid>
As you can expect, I'd just like to be able to pass the data from to card to the dialog so I can get my delete function functioning correctly.
If I've left out any info, or haven't provided enough code, or haven't explained something clearly enough, please let me know.
Many thanks ahead of time!
If I understand correctly, the DeleteDialogue is the dialog component that you are talking about.
If so, try passing a specific prop to the dialog and use it in the dialog.
Something like that:
{this.state.savedArticles.length ? (
<Grid>
{this.state.savedArticles.map((oneSavedArticle, i) => (
<Card style={savedArticleCard} key={i}>
<Typography variant="h5">{oneSavedArticle.headline}</Typography>
<Divider variant="middle" />
<Typography>{oneSavedArticle.snippet}</Typography>
<a href={oneSavedArticle.web_url} style={linkStyles}>
<Button style={buttonStyles}>READ IT</Button>
</a>
<DeleteDialogue id={oneSavedArticle.id} />
</Card>
))}
</Grid>
And in the dialog:
<div>
<Button
color="secondary"
variant="outlined"
onClick={this.handleClickOpen}
>
DELETE
</Button>
<Dialog
open={this.state.open}
TransitionComponent={Transition}
keepMounted
onClose={this.handleClose}
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle>
{"Delete this article?"}
</DialogTitle>
<Divider variant="middle" />
<DialogActions>
<Button onClick={this.handleClose} color="secondary">
NO
</Button>
<Button
onClick={() => this.handleDelete(this.props.id)}
color="primary"
>
YES
</Button>
</DialogActions>
</Dialog>
</div>
I think its suppose to work that way..

multiple modal in .map

i am in a .map an i want to creat on modal in each div generate by the .map
return (
{this.state.DataBook.map(function (item, i) {
return ( <div>
<Button color="danger" onClick={this.toggleModal}>test Modal</Button>
<Modal isOpen={this.state.modal} toggle={this.toggleModal} className={this.props.className} external={externalCloseBtn}>
<ModalHeader>Modal title</ModalHeader>
<ModalBody>
<b>{item.nom}</b><br />
Lorem ipsum
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.toggleModal}>Do Something</Button>{' '}
</ModalFooter>
</Modal>
</div>)},this)}
In order to do that i call toggleModal
toggleModal() {
this.setState({
modal: !this.state.modal
});
}
with this i have all my modal with the same content i don't know how to fix it.
Any idea?
Here's a working codeSandbox running this : https://codesandbox.io/s/rmxp8nxm74
To be able to individually identify events coming from JSX made by mapping an array, you have to pass an identifier to the called openModal method :
openModal = id => {
this.setState({ openedModal: id });
};
closeModal = () => {
this.setState({ openedModal: null });
};
render() {
return this.state.DataBook.map((item, i) => (
<div key={item.id}>
<Button color="danger" onClick={() => this.openModal(item.id)}>
test Modal
</Button>
<Modal
isOpen={this.state.openedModal === item.id}
toggle={this.closeModal}
>
<ModalHeader>Modal title</ModalHeader>
<ModalBody>
<b>{item.nom}</b>
<br />
Lorem ipsum
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.closeModal}>
Do Something
</Button>
</ModalFooter>
</Modal>
</div>
));
}
Edit : I created distinct open/close methods so you'll be able to easily close the active modal with an external call (no param to pass).
Edit 2 : You have also an alternative option : having a single dynamic Modal passing it a state set onClick (could be better in performance but harder to manage different click events in modal)
You can have activeModalIndex in your state which can be used to show the actual modal. Check the activeModalIndex when you open your modal.
this.state.DataBook.map(function (item, index) {
return (
<div>
<Button color="danger" onClick={() => this.toggleModal(index)}>test Modal</Button>
<Modal isOpen={(this.state.activeModalIndex === index) && this.state.modal} toggle={()=> this.toggleModal(index)} className={this.props.className} external={externalCloseBtn}>
...
</Modal>
</div>
You can toggle modal
toggleModal(activeIndex) {
this.setState({
modal: !this.state.modal,
activeModalIndex: activeIndex
});
}
Hope this helps.

Categories