I'm using Redux and material-ui
I'm trying to run Dialog with <Slide direction="up"/> animation using this attribute: TransitionComponent
email is a state value that came from reducer and changes when I enter value on TextField
When I try to enter some value on, animation plays but, I want to play it only one time.

interface IProps extends WithStyles<typeof styles> {
// ...
setEmail: (email: string) => void;
email: string;
// ...
}
const LoginDialog: React.SFC<IProps> = props => {
const handleClose = () => {
props.setIsOpen(false);
};
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
props.setPassword(event.target.value);
};
const handlePasswordVisibility = () => {
props.setPasswordVisibility(!props.passwordVisibility);
};
const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
props.setEmail(event.target.value);
};
return (
<div>
<Dialog
open={props.isOpen}
//dialog plays animation when props.isOpen changes
TransitionComponent={props => <Slide direction="up" {...props} />}
onClose={handleClose}
aria-labelledby="login-dialog-slide-title"
aria-describedby="login-dialog-slide-description"
disableBackdropClick={true}
keepMounted
>
<DialogTitle id="login-dialog-slide-title">
<FormattedMessage id="logindialog_title" />
</DialogTitle>
<DialogContent>
<TextField value={props.email} onChange={handleEmailChange} autoFocus type="email" label={<FormattedMessage id="logindialog_email" />}/>
<TextField type="password" label={<FormattedMessage id="logindialog_password" />} />
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
<FormattedMessage id="logindialog_cancle" />
</Button>
<Button onClick={handleClose} color="primary">
<FormattedMessage id="logindialog_ok" />
</Button>
</DialogActions>
</Dialog>
</div>
);
};
export default withStyles(styles)(withRouter(LoginDialog));
I updated my container which has mapStateToProps and action, reducer for email and also you can see my full code here: codesandbox.io/s/nkrmw3wjxj
import { connect } from "react-redux";
import { ICombineReducersState } from "../../../reducers";
import LoginDialog from "./LoginDialog";
import {
setIsOpen,
setPassword,
setPasswordVisibility,
setEmail,
setNickname,
DuplicatedEmail,
setIsEmailDuplicated
} from "../../../actions";
const mapStateToProps = (state: ICombineReducersState) => ({
isOpen: state.open.isOpen,
password: state.password.password,
passwordVisibility: state.password.passwordVisibility,
email: state.email.email,
isPasswordError: state.password.isPasswordError,
isEmailError: state.email.isEmailError,
isEmailDuplicated: state.email.isEmailDuplicated
});
const mapDispatchToProps = (dispatch: any) => ({
setIsOpen: (isOpen: boolean) => dispatch(setIsOpen(isOpen)),
setPassword: (password: string) => dispatch(setPassword(password)),
setPasswordVisibility: (passwordVisibility: boolean) =>
dispatch(setPasswordVisibility(passwordVisibility)),
setEmail: (email: string) => dispatch(setEmail(email)),
setNickname: (nickname: string) => dispatch(setNickname(nickname)),
DuplicatedEmail: () => dispatch(DuplicatedEmail()),
setIsEmailDuplicated: (isEmailDuplicated: boolean) =>
dispatch(setIsEmailDuplicated(isEmailDuplicated))
});
export const LoginDialogContainer = connect(
mapStateToProps,
mapDispatchToProps
)(LoginDialog);
export const SET_EMAIL = "SET_EMAIL";
export const SET_IS_EMAIL_DUPLICATED = "SET_IS_EMAIL_DUPLICATED";
import axios from "axios";
import config from "../config";
export interface IEmailAction {
email: string;
type: string;
isEmailDuplicated: boolean;
}
export const setEmail = (email: string) => {
return {
email,
type: SET_EMAIL,
} as IEmailAction;
};
export const setIsEmailDuplicated = (isEmailDuplicated: boolean) => {
return {
isEmailDuplicated,
type: SET_IS_EMAIL_DUPLICATED,
} as IEmailAction;
}
export const DuplicatedEmail = () => (dispatch: any):boolean => {
axios.get(`${config.REACT_APP_SERVER_URL}/users/email`)
.then(res => {
if (res.data.message.length >= 1) {
return dispatch(setIsEmailDuplicated(true));
}
})
.catch(err => {
console.log(err.response)
})
return dispatch(setIsEmailDuplicated(false));
}
import { IEmailAction, SET_EMAIL, SET_IS_EMAIL_DUPLICATED } from "../actions";
export interface IEmailState {
email: string;
isEmailError: boolean;
isEmailDuplicated: boolean;
}
const createEmpty = () => ({
email: "",
isEmailError: false,
isEmailDuplicated: false,
});
const emailRegex = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
export const emailReducer = (state = createEmpty(), action: IEmailAction) => {
switch (action.type) {
case SET_EMAIL: {
return {
email: action.email,
isEmailError: !validateEmail(action.email),
isEmailDuplicated: false,
} as IEmailState;
}
case SET_IS_EMAIL_DUPLICATED: {
return {
email: state.email,
isEmailError: true,
isEmailDuplicated: action.isEmailDuplicated,
} as IEmailState;
}
default:
return state;
}
};
const validateEmail = (email: string):boolean => {
if (emailRegex.test(email)) {
return true;
}
return false;
}
Please let me know if you need more info about it.
Thanks.
The following line was the problem:
TransitionComponent={props => <Slide direction="up" {...props} />}
By defining this inline it means that the TransitionComponent will look like a new component type with each re-render which then causes that portion of the Dialog to re-mount and therefore redo the transition.
This is easily fixed by defining this outside of your component function as a stable component type (I've called it TransitionComponent below) and then using this for the TransitionComponent Dialog prop:
const TransitionComponent = props => <Slide direction="up" {...props} />;
const LoginDialog: React.SFC<IProps> = props => {
...
return (
<div>
<Dialog
open={props.isOpen}
TransitionComponent={TransitionComponent}
...
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With