Can't get jest test to fire button onclick - reactjs

I am completely new to both react and testing and am a bit stumped.
I was just wondering if someone could tell me why my test fails. I assume I making a basic mistake in how this should work.
I am trying to test a log in page. At the moment I am just trying to get my test to fire an onclick from a button and check that that a function has been called.
The log in component code can be seen below.
import React, { Component, Fragment } from "react";
import { Redirect } from "react-router-dom";
// Resources
//import logo from "assets/img/white-logo.png";
//import "./Login.css";
// Material UI
import {
withStyles,
MuiThemeProvider,
createMuiTheme
} from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import TextField from "#material-ui/core/TextField";
import Person from "#material-ui/icons/Person";
import InputAdornment from "#material-ui/core/InputAdornment";
// Custom Components
import Loading from "components/Loading/Loading.jsx";
// bootstrat 1.0
import { Alert, Row } from "react-bootstrap";
// MUI Icons
import LockOpen from "#material-ui/icons/LockOpen";
// remove
import axios from "axios";
// API
import api2 from "../../helpers/api2";
const styles = theme => ({
icon: {
color: "#fff"
},
cssUnderline: {
color: "#fff",
borderBottom: "#fff",
borderBottomColor: "#fff",
"&:after": {
borderBottomColor: "#fff",
borderBottom: "#fff"
},
"&:before": {
borderBottomColor: "#fff",
borderBottom: "#fff"
}
}
});
const theme = createMuiTheme({
palette: {
primary: { main: "#fff" }
}
});
class Login extends Component {
constructor(props, context) {
super(props, context);
this.state = {
username: "",
password: "",
isAuthenticated: false,
error: false,
toggle: true,
// loading
loading: false
};
}
openLoading = () => {
this.setState({ loading: true });
};
stopLoading = () => {
this.setState({ loading: false });
};
toggleMode = () => {
this.setState({ toggle: !this.state.toggle });
};
handleReset = e => {
const { username } = this.state;
this.openLoading();
api2
.post("auth/admin/forgotPassword", { email: username })
.then(resp => {
this.stopLoading();
console.log(resp);
})
.catch(error => {
this.stopLoading();
console.error(error);
});
};
handleSubmit = event => {
event.preventDefault();
localStorage.clear();
const cred = {
username: this.state.username,
password: this.state.password
};
api2
.post("auth/admin", cred)
.then(resp => {
console.log(resp);
localStorage.setItem("api_key", resp.data.api_key);
localStorage.setItem("username", cred.username);
return this.setState({ isAuthenticated: true });
})
.catch(error => {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
console.log(error.request);
} else {
console.log("Error", error.message);
}
console.log(error.config);
return this.setState({ error: true });
});
};
handleInputChange = event => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
};
forgotPassword = () => {
console.log("object");
};
render() {
const { error } = this.state;
const { isAuthenticated } = this.state;
const { classes } = this.props;
if (isAuthenticated) {
return <Redirect to="/home/dashboard" />;
}
return (
<div className="login-page">
<video autoPlay muted loop id="myVideo">
<source
src=""
type="video/mp4"
/>
</video>
<div className="videoOver" />
<div className="midl">
<Row className="d-flex justify-content-center">
<img src={''} className="Login-logo" alt="logo" />
</Row>
<br />
<Row className="d-flex justify-content-center">
{error && (
<Alert style={{ color: "#fff" }}>
The username/password entered is incorrect. Try again!
</Alert>
)}
</Row>
<MuiThemeProvider theme={theme}>
<Row className="d-flex justify-content-center">
<TextField
id="input-username"
name="username"
type="text"
label="username"
value={this.state.username}
onChange={this.handleInputChange}
InputProps={{
className: classes.icon,
startAdornment: (
<InputAdornment position="start">
<Person className={classes.icon} />
</InputAdornment>
)
}}
/>
</Row>
{this.state.toggle ? (
<Fragment>
<br />
<Row className="d-flex justify-content-center">
<TextField
id="input-password"
name="password"
type="password"
label="pasword"
value={this.state.password}
onChange={this.handleInputChange}
className={classes.cssUnderline}
InputProps={{
className: classes.icon,
startAdornment: (
<InputAdornment position="start">
<LockOpen className={classes.icon} />
</InputAdornment>
)
}}
/>
</Row>
</Fragment>
) : (
""
)}
</MuiThemeProvider>
<br />
<Row className="d-flex justify-content-center">
{this.state.toggle ? (
<Button
className="button login-button"
data-testid='submit'
type="submit"
variant="contained"
color="primary"
onClick={this.handleSubmit}
name = "logIn"
>
Login
</Button>
) : (
<Button
className="button login-button"
type="submit"
variant="contained"
color="primary"
onClick={this.handleReset}
>
Reset
</Button>
)}
</Row>
<Row className="d-flex justify-content-center">
<p onClick={this.toggleMode} className="text-link">
{this.state.toggle ? "Forgot password?" : "Login"}
</p>
</Row>
</div>
<Loading open={this.state.loading} onClose={this.handleClose} />
</div>
);
}
}
export default withStyles(styles)(Login);
My current test which fails.
import React from 'react'
import {render, fireEvent, getByTestId} from 'react-testing-library'
import Login from './login'
describe('<MyComponent />', () => {
it('Function should be called once', () => {
const functionCalled = jest.fn()
const {getByTestId} = render(<Login handleSubmit={functionCalled} />)
const button = getByTestId('submit');
fireEvent.click(button);
expect(functionCalled).toHaveBeenCalledTimes(1)
});
});

I'm also fairly new to React Testing Library, but it looks like your button is using this.handleSubmit, so passing your mock function as a prop won't do anything. If you were to pass handleSubmit in from some container, then I believe your tests, as you currently have them, would pass.

Related

Failed prop type: Material-UI: Either `children`, `image`, `src` or `component` prop must be specified in ForwardRef(CardMedia)

Whenever I'm trying to post new Screams in my page. I'm not getting the image, title and content of the card. I need to reload to get that.
It is saying failed prop types means after posting a scream I'm not getting the value from prop types but after a reload, everything seems to be fine. I don't know what is wrong is going. Please have a look at my code.
This is my Screams.js from where i'm showing my Screams
Scream.js
const styles = {
card: {
position: "relative",
display: "flex",
marginBottom: 20,
},
image: {
minWidth: 150,
},
content: {
padding: 25,
objectFit: "cover",
},
};
class Scream extends Component {
render() {
dayjs.extend(relativeTime);
const {
classes,
scream: {
body,
createdAt,
userImage,
userHandle,
screamId,
likeCount,
commentCount,
},
user: {
authenticated,
credentials: { handle },
},
} = this.props;
const deleteButton =
authenticated && userHandle === handle ? (
<DeleteScream screamId={screamId} />
) : null;
const likeButton = !authenticated ? (
<Link to="/login">
<MyButton tip="Like">
<FavoriteBorder color="primary" />
</MyButton>
</Link>
) : this.likedScream() ? (
<MyButton tip="Undo like" onClick={this.unlikeScream}>
<FavoriteIcon color="primary" />
</MyButton>
) : (
<MyButton tip="Like" onClick={this.likeScream}>
<FavoriteBorder color="primary" />
</MyButton>
);
return (
<Card className={classes.card}>
<CardMedia
image={userImage}
title="Profile Image"
className={classes.image}
/>
<CardContent className={classes.content}>
<Typography variant="h5" component={Link} to={`/users/${userHandle}`}>
{userHandle}
</Typography>
{deleteButton}
<Typography variant="body2" color="textSecondary">
{dayjs(createdAt).fromNow()}
</Typography>
<Typography variant="body1">{body}</Typography>
{likeButton}
<span> {likeCount} Likes </span>
<MyButton tip="comments">
<ChatIcon color="primary" />
</MyButton>
<span> {commentCount} Comments </span>
</CardContent>
</Card>
);
}
}
Scream.propTypes = {
likeScream: PropTypes.func.isRequired,
unlikeScream: PropTypes.func.isRequired,
user: PropTypes.object.isRequired,
scream: PropTypes.object.isRequired,
classes: PropTypes.object.isRequired,
};
const mapStateToprops = (state) => ({
user: state.user,
});
const mapActionToProps = {
likeScream,
unlikeScream,
};
export default connect(
mapStateToprops,
mapActionToProps
)(withStyles(styles)(Scream));
As u can see i have userImage, userHandle and body of the card in the props and it is showing it on my page.
But after posting a new Scream, i'm not getting the image, userHandle and body of the new Scream unless and until reload it.
PostScream.js
class PostScream extends Component {
state = {
open: false,
body: "",
errors: {}
}
componentWillReceiveProps(nextProps) {
if(nextProps.UI.errors) {
this.setState({
errors: nextProps.UI.errors
});
}
if(!nextProps.UI.errors && !nextProps.UI.loading) {
this.setState({
body: "",
open: false,
errors: {}
})
}
}
handleOpen = () => {
this.setState({ open: true })
};
handleClose = () => {
this.props.clearErrors();
this.setState({ open: false, errors: {} });
};
handleChange = (event) => {
this.setState({ [event.target.name]: event.target.value })
}
handleSubmit = (event) => {
event.preventDefault();
this.props.postScream({ body: this.state.body })
}
render() {
const { errors } = this.state;
const { classes, UI: { loading }} = this.props;
return (
<Fragment>
<MyButton onClick= { this.handleOpen } tip="Post a Scream!">
<AddIcon />
</MyButton>
<Dialog
open= { this.state.open }
onClose= { this.handleClose }
fullWidth
maxWidth = "sm"
>
<MyButton
tip="Close"
onClick={this.handleClose}
tipClassName={classes.closeButton}
>
<CloseIcon />
</MyButton>
<DialogTitle> Post a new Scream </DialogTitle>
<DialogContent>
<form onSubmit= { this.handleSubmit }>
<TextField
name="body"
type="text"
label="SCREAM!"
multiline
rows="3"
placeholder= "What's on your mind!"
error={ errors.body ? true: false }
helperText={ errors.body }
className={classes.TextField}
onChange={ this.handleChange }
fullWidth
/>
<Button
type="submit"
variant="contained"
color="primary"
className={classes.submitButton}
disabled={ loading }
>
Submit
{loading && (
<CircularProgress size={30} className={ classes.progressSpinner } />
)}
</Button>
</form>
</DialogContent>
</Dialog>
</Fragment>
)
}
}
PostScream.propTypes = {
postScream: PropTypes.func.isRequired,
clearErrors: PropTypes.func.isRequired,
UI: PropTypes.object.isRequired
}
const mapStateToProps = (state) => ({
UI: state.UI
});
export default connect(
mapStateToProps,
{ postScream, clearErrors }
)(withStyles(styles)(PostScream));
After posting a new Scream, i am getting Scream like this:-
I was having the same issue, I was able to fix it by adding component='img' to my CardMedia.
<CardMedia
image={userImage}
title="Profile Image"
className={classes.image}
component='img'
/>
Specifying the component as an img "Hides" the images since CardMedia is a div and your css is applied to the div. Add component = "div" to CardMedia.
<CardMedia
image={userImage}
title="Profile Image"
className={classes.image}
component='div'
/>

Was Form.create() from antd replaced?

i am trying to create a login page in react using antd. I found a tutorial for doing this,but i belive that it is outdated, because it gives an error. I read on a forum that form.create() is not available anymore, but i don't know how to replace it.Here is my code:
import { Form, Icon, Input, Button, Spin } from 'antd';
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <Icon type="loading" style={{ fontSize: 24 }} spin />;
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem>
{getFieldDecorator('userName', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem>
<FormItem>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const WrappedNormalLoginForm = Form.create()(NormalLoginForm);
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(WrappedNormalLoginForm);
When i try to run it, i get an error that says:
TypeError: _antd.Form.create(...) is not a function
How can i rewrite the code in order to make it run?
AntD has removed Form.create in v4 and you can replace the above code with the modified API structure like
class NormalLoginForm extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
});
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
const { getFieldDecorator } = this.props.form;
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onSubmit={this.handleSubmit} className="login-form">
<FormItem name='userName' rules={[{ required: true, message: 'Please input your username!' }]}>
<Input prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
</FormItem>
<FormItem name="password" rules={[{ required: true, message: 'Please input your Password!' }]}>
<Input prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalLoginForm);
Please see the migration guidelines for more details
I think you are using Antd v4.*. Form.create({}) is no more in latest version, so you have to your code as follow to make it working. V4 has. major changes so you have to go through migration guidelines
import { Form, Input, Button, Spin } from 'antd';
import { LoadingOutlined, UserOutlined, LockOutlined } from "#ant-design/icons";
import { connect } from 'react-redux';
import { NavLink } from 'react-router-dom';
import * as actions from '../store/actions/auth';
const FormItem = Form.Item;
const antIcon = <LoadingOutlined style={{ fontSize: 24 }} />;
class NormalLoginForm extends React.Component {
handleFinish = (values) => {
this.props.onAuth(values.userName, values.password);
this.props.history.push('/');
}
render() {
let errorMessage = null;
if (this.props.error) {
errorMessage = (
<p>{this.props.error.message}</p>
);
}
return (
<div>
{errorMessage}
{
this.props.loading ?
<Spin indicator={antIcon} />
:
<Form onFinish={this.handleFinish} className="login-form">
<FormItem name="userName" rules={[{ required: true, message: 'Please input your username!' }]}>
<Input prefix={<UserOutlined style={{ color: 'rgba(0,0,0,.25)' }} />} placeholder="Username" />
)}
</FormItem name="password" rules={[{ required: true, message: 'Please input your Password!' }]}>
<FormItem>
<Input prefix={<LockOutlined style={{ color: 'rgba(0,0,0,.25)' }} />} type="password" placeholder="Password" />
)}
</FormItem>
<FormItem>
<Button type="primary" htmlType="submit" style={{marginRight: '10px'}}>
Login
</Button>
Or
<NavLink
style={{marginRight: '10px'}}
to='/signup/'> signup
</NavLink>
</FormItem>
</Form>
}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading,
error: state.error
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: (username, password) => dispatch(actions.authLogin(username, password))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NormalLoginForm);

how to remove error messages asynchronously in React

I'm fetching an array of errors, one of them says "Username is taken" and the other one says "Password must be at least 5 chars".
It's displayed like this in React.
{this.props.auth.errors ? (
this.props.auth.errors.map( (err, i) => (
<div key={i} style={{color: 'red'}}>
{err}
</div>
))
):(
null
)}
this.props.auth.errors is an array containing the error messages, after registerUser gets called.
Actions.js
export const registerUser = (userData) => dispatch => {
Axios
.post('/users/register', userData)
.then( res => {
const token = res.data.token;
// console.log(token);
// pass the token in session
sessionStorage.setItem("jwtToken", token);
// set the auth token
setAuthToken(token);
// decode the auth token
const decoded = jwt_decode(token);
// pass the decoded token
dispatch(setCurrentUser(decoded))
// this.props.history.push("/dashboard")
}).catch( err => {
// console.log(err.response.data.error[0].msg)
Object.keys(err.response.data.error).forEach( (key) => {
dispatch({
type: GET_ERRORS,
payload: err.response.data.error[key].msg
})
})
})
};
How would i be able to remove the error, if for example the user does make a password that is minimum of 5 chars, or uses a username that does exist ? You know what i mean ? Also could this be done asynchronously ? As if you were to sign in or register on a top end social media website where it shows the error on async and gos away once you complied to the error message.
Full react code
SignUp.js
import React, { Component } from "react";
import {connect} from 'react-redux';
import {registerUser} from '../actions/authActions';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Grid from '#material-ui/core/Grid';
import PropTypes from "prop-types";
import Typography from '#material-ui/core/Typography';
class SignUp extends Component{
constructor() {
super();
this.state = {
formData:{
email:'',
username:'',
password:'',
passwordConf: "",
isAuthenticated: false,
},
errors:{},
passErr:null
}
}
componentDidMount() {
// console.log(this.props.auth);
if (this.props.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
}
// this line is magic, redirects to the dashboard after user signs up
componentWillReceiveProps(nextProps) {
if (nextProps.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
}
handleChange = (e) => {
e.preventDefault();
const {formData} = this.state;
this.setState({
formData: {
...formData,
[e.target.name]: e.target.value
}
});
}
handleSubmit = (e) => {
e.preventDefault();
const {formData} = this.state;
const {username, email, password, passwordConf} = formData;
this.setState({
username: this.state.username,
password: this.state.password,
passwordConf: this.state.passwordConf,
email: this.state.email
});
const creds = {
username,
email,
password
}
console.log(creds);
if (password === passwordConf) {
this.props.registerUser(creds, this.props.history);
} else {
this.setState({passErr: "Passwords Don't Match"})
}
}
render(){
return(
<div>
<Grid container justify="center" spacing={0}>
<Grid item sm={10} md={6} lg={4} style={{ margin:'20px 0px'}}>
<Typography variant="h4" style={{ letterSpacing: '2px'}} >
Sign Up
</Typography>
{this.props.auth.errors ? (
this.props.auth.errors.map( (err, i) => (
<div key={i} style={{color: 'red'}}>
{err}
</div>
))
):(
null
)}
{this.state.passErr && (
<div style={{color: 'red'}}>
{this.state.passErr}
</div>
)}
<form onSubmit={this.handleSubmit}>
<TextField
label="Username"
style={{width: '100%' }}
name="username"
value={this.state.username}
onChange={this.handleChange}
margin="normal"
/>
<br></br>
<TextField
label="Email"
className=""
style={{width: '100%' }}
name="email"
value={this.state.email}
onChange={this.handleChange}
margin="normal"
/>
<br></br>
<TextField
label="Password"
name="password"
type="password"
style={{width: '100%' }}
className=""
value={this.state.password}
onChange={this.handleChange}
margin="normal"
/>
<br></br>
<TextField
label="Confirm Password"
name="passwordConf"
type="password"
style={{width: '100%' }}
className=""
value={this.state.passwordConf}
onChange={this.handleChange}
margin="normal"
/>
<br></br>
<br></br>
<Button variant="outlined" color="primary" type="submit">
Sign Up
</Button>
</form>
</Grid>
</Grid>
</div>
)
}
}
SignUp.propTypes = {
registerUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
auth: state.auth
})
const mapDispatchToProps = (dispatch) => ({
registerUser: (userData) => dispatch(registerUser(userData))
})
export default connect(mapStateToProps, mapDispatchToProps)(SignUp)
AuthReducer
import {SET_CURRENT_USER, GET_ERRORS} from '../actions/types';
import isEmpty from '../actions/utils/isEmpty';
const initialState = {
isAuthenticated: false,
errors: []
}
export default (state = initialState, action) => {
switch (action.type) {
case SET_CURRENT_USER:
return{
...state,
isAuthenticated: !isEmpty(action.payload),
user:action.payload
}
case GET_ERRORS:
console.log(action.payload)
// allows for us to loop through an array of errors.
return Object.assign({}, state, {
errors: [...state.errors, action.payload]
})
default:
return state;
}
}
a UI example of how this plays out
Could you clear the errors from your state inside the GET_ERRORS case in your reducer? Basically change your case GET_ERRORS to this:
case GET_ERRORS:
console.log(action.payload)
// allows for us to loop through an array of errors.
return Object.assign({}, state, {
errors: [action.payload]
})
Then in your action creator, do this in your catch instead:
const errors = [];
Object.keys(err.response.data.error).forEach( (key) => {
errors.push(err.response.data.error[key].msg)
})
dispatch({
type: GET_ERRORS,
payload: errors,
})
To get the errors all on individual lines do something like this in your render function:
{this.props.auth.errors ? (
this.props.auth.errors.map( (err, i) => (
<div key={i} style={{color: 'red'}}>
{err}
</div>
<br />
))
):(
null
)}

react- setState doesn't clear textbox

I want to clear textbox fields on success or error. how can I do that.?
On success or error, calling a function to show the snack bar. Within that, I'm updating the state. but it is not working fine.
any help appreciated.!
function
openSnackbar = ({ message }) => {
this.setState({
open: true,
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: '',
message
});
};
textfield
<TextField
id="cp_currentPassword"
label="Current Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_currentPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>;
component
import React, { Component } from 'react'
import withStyles from "#material-ui/core/styles/withStyles";
import CircularProgress from '#material-ui/core/CircularProgress';
import TextField from '#material-ui/core/TextField';
import GridItem from "../uiComponents/Grid/GridItem.jsx";
import GridContainer from "../uiComponents/Grid/GridContainer.jsx";
import Button from "../uiComponents/CustomButtons/Button.jsx";
import Card from "../uiComponents/Card/Card.jsx";
import CardHeader from "../uiComponents/Card/CardHeader.jsx";
import CardBody from "../uiComponents/Card/CardBody.jsx";
import CardFooter from "../uiComponents/Card/CardFooter.jsx";
import Snackbar from '#material-ui/core/Snackbar';
import CloseIcon from '#material-ui/icons/Close';
import { Redirect } from 'react-router-dom'
import IconButton from '#material-ui/core/IconButton';
import { connect } from 'react-redux'
import { compose } from 'redux'
import { changePassword } from '../../store/actions/auth'
const styles = {
textField: {
fontSize: '5px'
},
};
class ChangePassword extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
open: false,
message: '',
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
}
}
componentDidUpdate = (prevProps) => {
const { authError } = this.props;
console.log(authError)
if (authError != prevProps.authError) {
this.setState(
{
loading: false,
message: authError,
open: true
})
}
};
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
openSnackbar = ({ message }) => {
this.setState({
open: true,
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: '',
message
});
};
handleSubmit = (e) => {
e.preventDefault();
let curpass = this.state.cp_currentPassword
let newpass = this.state.cp_newPassword
this.setState({ loading: true });
this.props.changePassword(curpass, newpass, this.passwordUpdated)
}
passwordUpdated = () => {
this.setState({
message: 'Password changed Successfully.!',
open: true,
loading: false,
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
});
};
render() {
const { classes, auth, authError } = this.props;
console.log(authError)
const { loading } = this.state;
const message = (
<span
id="snackbar-message-id"
dangerouslySetInnerHTML={{ __html: this.state.message }}
/>
);
if (!auth.uid) return <Redirect to='/signin' />
return (
<div>
{/* {authError ? this.openSnackbar({ message: '{authError}' }) : null} */}
{/* {authError && !this.state.open ? this.openSnackbar({ message: '{authError}' }) : null} */}
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<Card>
<CardHeader color="warning">
<h4 className={classes.cardTitleWhite}>Change Password</h4>
</CardHeader>
<form >
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CardBody>
<GridContainer>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_currentPassword"
label="Current Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_currentPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_newPassword"
label="New Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_newPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
<GridItem xs={12} sm={12} md={12}>
<TextField
id="cp_confirmPassword"
label="Confirm Password"
type="password"
fullWidth
className={classes.textField}
value={this.state.cp_confirmPassword}
onChange={this.handleChange}
margin="normal"
required={true}
/>
</GridItem>
</GridContainer>
</CardBody>
<CardFooter>
<Button color="warning" onClick={(e) => this.handleSubmit(e)} disabled={loading}>
{loading && <CircularProgress style={{ color: 'white', height: '20px', width: '20px', marginRight: '10px' }} />}
Change Password
</Button>
</CardFooter>
</GridItem>
</GridContainer>
</form>
</Card>
</GridItem>
</GridContainer>
<Snackbar
open={this.state.open}//{!!this.props.authError}//
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
message={message}
variant="error"
onClose={() => this.setState({ open: false, message: '' })}
action={
<IconButton
key="close"
aria-label="Close"
color="inherit"
className={classes.close}
onClick={() =>
// clearAuthError
this.setState({ open: false, message: '' })
}
>
<CloseIcon className={classes.icon} />
</IconButton>
}
autoHideDuration={3000}
/>
</div>
)
}
}
const mapstateToProps = (state) => {
return {
auth: state.firebase.auth,
authError: state.authroot.autherr
}
}
const mapDispatchtoProps = (dispatch, getState) => {
return {
changePassword: (currentPassword, newPassword, passwordUpdated) => { dispatch(changePassword(currentPassword, newPassword, passwordUpdated)) },
}
}
export default compose(
withStyles(styles),
connect(mapstateToProps, mapDispatchtoProps)
)(ChangePassword);
In your handleSubmit function don't pass this.passwordUpdated
handleSubmit = (e) => {
e.preventDefault();
let curpass = this.state.cp_currentPassword
let newpass = this.state.cp_newPassword
this.setState({ loading: true });
this.props.changePassword(curpass, newpass, this.passwordUpdated) //Remove this.passwordUpdated from here
}
Instead of ComponentDidUpdate method you should use ComponentWillReceiveProps
componentWillReceiveProps(nextProps) {
console.log('componentWillReceiveProps', nextProps);
if (this.props.authError !== nextProps.authError) {
// here you can make your textbox empty
this.setState({
cp_currentPassword: '',
cp_newPassword: '',
cp_confirmPassword: ''
});
}
}
Note: In new version of react you must use static getDerivedStateFromProps() instead of componentWillReceiveProps.

i need to change the login to add to cart when the user login in react

is their any possible way to change the state of header that track the user is logged in or not and show the button as per it if login in then show add to cart and if not show login
i have checked the session storage where i have stored my data if their is data change the state if not don't in component did mount but the component did mount will not re render the app we have to do manual refresh how to keep track of it
import React from 'react';
import { NavLink } from 'react-router-dom';
import { Navbar, Nav, NavItem } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap';
class Header extends React.Component {
state = {
login: false
}
componentDidMount() {
const data = sessionStorage.getItem("login");
if (data) {
this.setState({ login: !this.state.login })
}
}
componentDidUpdate(prevProps, prevState) {
if (prevState.login !== this.state.login) {
this.setState({ login: true })
}
}
render() {
return (
<Navbar inverse collapseOnSelect>
<Navbar.Header>
<Navbar.Brand>
<NavLink to="/" >Home</NavLink>
</Navbar.Brand>
<Navbar.Toggle />
</Navbar.Header>
<Navbar.Collapse>
<Nav>
<LinkContainer to="/shop">
<NavItem eventKey={1}>Shop</NavItem>
</LinkContainer>
</Nav>
<Nav pullRight>
{this.state.login ?
<LinkContainer to="/addtocart">
<NavItem eventKey={1}>Add To cart</NavItem>
</LinkContainer>
:
<LinkContainer to="/login">
<NavItem eventKey={1}>Login</NavItem>
</LinkContainer>
}
<LinkContainer to="/signip">
<NavItem eventKey={1}>Sigup</NavItem>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Navbar>
)
}
}
export default Header;
and in login
import React from 'react';
import { FormGroup, ControlLabel, FormControl } from 'react-bootstrap';
import { UserData } from '../../PostData';
import { ClipLoader } from 'react-spinners';
import { Redirect } from 'react-router-dom'
class Login extends React.Component {
state = {
username: undefined,
email: undefined,
loading: false,
redirect: false
}
onSubmit = (e) => {
e.preventDefault();
this.setState({ loading: true });
if (this.state.email && this.state.email !== '') {
try {
UserData(this.state.username, this.state.email).then(result => {
this.setState({ loading: false })
if (result.length) {
sessionStorage.setItem("login", JSON.stringify(result));
this.setState({ redirect: true })
} else {
alert("please enter the write email and username");
}
})
}
catch (e) {
console.log(e)
}
} else {
console.log("black")
}
}
onChangeHandle = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
render() {
if (this.state.redirect) {
return <Redirect to="/" />
}
return (
<div>
<div style={{ position: 'absolute', top: '20%', left: '50%', zIndex: '111111' }}>
<ClipLoader
sizeUnit={"px"}
size={150}
color={'#123abc'}
loading={this.state.loading}
/>
</div>
<div className="container">
<form onSubmit={this.onSubmit}>
<FormGroup
controlId="formBasicText"
// validationState={this.getValidationState()}
>
<ControlLabel>username</ControlLabel>
<FormControl
type="text"
placeholder="Enter username"
name="username"
onChange={this.onChangeHandle}
/>
<FormControl.Feedback />
<ControlLabel>Email</ControlLabel>
<FormControl
type="text"
name="email"
placeholder="Enter email"
onChange={this.onChangeHandle}
/>
<FormControl.Feedback />
<input type="submit" className="btn btn-primary" value="login"></input>
</FormGroup>
</form>
</div>
</div>
)
}
}
export default Login;
`
import React from 'react';
import { FormGroup, ControlLabel, FormControl } from 'react-bootstrap';
import { UserData } from '../../PostData';
import { ClipLoader } from 'react-spinners';
import { Redirect } from 'react-router-dom'
class Login extends React.Component {
state = {
username: undefined,
email: undefined,
loading: false,
redirect: false
}
onSubmit = (e) => {
e.preventDefault();
this.setState({ loading: true });
if (this.state.email && this.state.email !== '') {
try {
UserData(this.state.username, this.state.email).then(result => {
this.setState({ loading: false })
if (result.length) {
sessionStorage.setItem("login", JSON.stringify(result));
this.setState({ redirect: true })
} else {
alert("please enter the write email and username");
}
})
}
catch (e) {
console.log(e)
}
} else {
console.log("black")
}
}
onChangeHandle = (e) => {
this.setState({ [e.target.name]: e.target.value })
}
render() {
if (this.state.redirect) {
return <Redirect to="/" />
}
return (
<div>
<div style={{ position: 'absolute', top: '20%', left: '50%', zIndex: '111111' }}>
<ClipLoader
sizeUnit={"px"}
size={150}
color={'#123abc'}
loading={this.state.loading}
/>
</div>
<div className="container">
<form onSubmit={this.onSubmit}>
<FormGroup
controlId="formBasicText"
// validationState={this.getValidationState()}
>
<ControlLabel>username</ControlLabel>
<FormControl
type="text"
placeholder="Enter username"
name="username"
onChange={this.onChangeHandle}
/>
<FormControl.Feedback />
<ControlLabel>Email</ControlLabel>
<FormControl
type="text"
name="email"
placeholder="Enter email"
onChange={this.onChangeHandle}
/>
<FormControl.Feedback />
<input type="submit" className="btn btn-primary" value="login"></input>
</FormGroup>
</form>
</div>
</div>
)
}
}
export default Login;
`

Resources