How to get response data from ajax call in mapDispatchToProps - reactjs

I'm working on a login system. When user input email and password and click submit I'm calling my endpoint to verify the credentials with mapDispatchToProps, if login is correct I update the app state with token and auth:true, otherwise I need to publish the error. How can I read the response error message (that coming from api backend json response)?
I'm looking to read the response of the dispatched usersLogin() function.
import React, { Component } from "react";
import { connect } from "react-redux";
import { usersLogin } from "../../actions/index";
import { Link } from "react-router-dom";
import i18n from "../../i18n";
function mapDispatchToProps(dispatch) {
return {
usersLogin: login => dispatch(usersLogin(login))
};
}
class ConnectedLoginForm extends Component {
constructor() {
super();
this.state = {
email: "",
password: ""
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ [event.target.id]: event.target.value });
}
handleSubmit(event) {
event.preventDefault();
const { email } = this.state;
const { password } = this.state;
this.props.usersLogin({ email, password });
this.setState({ email: email, password: password });
}
render() {
const { email } = this.state;
const { password } = this.state;
console.log("this.props");
console.log(this.props.usersLogin);
return (
<form onSubmit={this.handleSubmit} className="login-form">
<div className="form-group">
<h2>{ i18n.t("Login") }</h2>
<input
type="text"
className="input-text"
id="email"
value={email}
onChange={this.handleChange}
placeholder="Email"
autoCorrect="off" autoCapitalize="none"
/>
<input
type="text"
className="input-text"
id="password"
value={password}
onChange={this.handleChange}
placeholder="Password"
autoCorrect="off" autoCapitalize="none"
/>
<button type="submit" className="button btn-primary">
Login
</button>
<div className="other-action">
<Link to="/logout">Registrati</Link>
<Link to="/logout">Password dimenticata</Link>
</div>
</div>
</form>
);
}
}
const Loginform = connect(null, mapDispatchToProps)(ConnectedLoginForm);
export default Loginform;
Sure, this is my login function, in a redux middleware:
const reqBody = { email: action.payload.email, password: action.payload.password };
const cfg = { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } };
axios.post(endpoint.users+'/login', qs.stringify(reqBody), cfg)
.then((result) => {
return dispatch({ type: "USERS_LOGIN_SUCCESS", payload : {token: result.data.token, auth: true } } );
})
.catch((err) => {
return dispatch({ type: "USERS_LOGIN_ERROR", payload : {token: '', auth: false } } );
})

Thank you everyone, I've changed my approach. I've installed react-toastify and I've putted the error notification in the catch block of the ajax call in the middleware.
.catch((err) => {
toast.error(i18n.t(err.response.data.errorMessage) );
return dispatch({ type: "USERS_LOGIN_ERROR", payload : { user : { token: '', auth: false } } } );
})
The solution seems to fit my need for now.

Related

React-Redux: Unhandled Rejection (TypeError): dispatch is not a function

I am struggling with Login page.
This is the actions/login.js:
export const login = (username, password) => (dispatch) => {
return AuthService.login(username, password).then(
(data) => {
debugger;
dispatch({
type: LOGIN_SUCCESS,
payload: { user: data },
});
return Promise.resolve();
},
(error) => {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
dispatch({
type: LOGIN_FAIL,
});
dispatch({
type: SET_MESSAGE,
payload: message,
});
return Promise.reject();
}
);
};
This is my AuthService.js :
import {BASE_URL} from "../constants/globalConstants";
import axios from "axios";
export const USER_INFO = 'USER_INFO';
const loginEndpoint = BASE_URL + "authenticate";
class AuthService {
login(username, password) {
debugger;
return axios
.post(BASE_URL + "authenticate", { username, password })
.then((response) => {
if (response.data.jwtToken) {
localStorage.setItem(USER_INFO, JSON.stringify(response.data));
}
return response.data;
});
}
logout() {
localStorage.removeItem(USER_INFO);
}
register(username, email, password) {
return axios.post(BASE_URL + "register", {
username,
email,
password,
});
}
}
export default new AuthService();
And finally the Login.js:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from "react-router-dom";
import { Container, Row, Col, Card, CardBody, FormGroup, Label, Input, Button } from "reactstrap";
import { AvForm, AvField } from "availity-reactstrap-validation";
import axios from 'axios'
import { bindActionCreators } from "redux";
import { selectedSidebarStyle } from "../../actions/sidebarStyleAction";
import { connect } from "react-redux";
import tokenIsValid from './authrorization/JwtAuthorization'
import './../../static/css/Auth.css'
import { BASE_URL } from "../../constants/globalConstants";
import AuthService from "../../services/AuthService";
import { login } from "../../actions/auth";
export const USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser';
export const JWT_AUTH_TOKEN = 'AUTH_TOKEN';
export const USER_INFO = 'USER_INFO';
const style = { border: '1px solid #FB3E3E' }
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
userAuth: false,
loading: false,
}
}
handleFieldChange = (event) => {
this.setState({
[event.target.name]: event.target.value
})
}
// this.props.history.push(`/welcome/${this.state.username}`)
requestLogin = () => {
const loginEndpoint = BASE_URL + "authenticate";
axios({
method: 'post',
url: loginEndpoint,
data: {
username: this.state.username,
password: this.state.password
}
}).then((response) => {
if (response.data !== null) {
sessionStorage.setItem(USER_INFO, JSON.stringify(response.data));
}
}, (error) => {
console.log("Unsuccessful login request")
})
}
authHeader() {
const user = JSON.parse(localStorage.getItem(USER_INFO));
if (user && user.jwtToken) {
return { Authorization: 'Bearer ' + user.jwtToken };
} else {
return {};
}
}
isUserLoggedIn() {
let user = window.sessionStorage.getItem(USER_INFO)
if (user === null) {
return false
}
return true;
}
getLoggedInUserName() {
let user = window.sessionStorage.getItem(USER_INFO)
if (user === null) {
return ''
}
return user
}
/*
* TODO: See where to use the logout and how to redirect the user to the login page in case JWT token is expired
* */
logout() {
sessionStorage.removeItem(USER_INFO);
}
handleSubmit = (e) => {
e.preventDefault();
const {dispatch} = this.props;
dispatch(login(this.state.username, this.state.password))
.then(() => {
window.location.reload();
})
.catch(() => {
this.setState({
loading: false
});
});
}
render() {
return (
<React.Fragment>
<div className="account-home-btn d-none d-sm-block">
<Link to="/" className="text-white"><i className="mdi mdi-home h1"></i></Link>
</div>
<section className="bg-account-pages height-100vh">
<img className={"hive-logo1"} src={require('./hive-logo.png')} alt="Logo" width="70px" height="60px" />
<div className="display-table">
<div className="display-table-cell">
<Container>
<Row className="justify-content-center">
<Col lg={5}>
<Card className="account-card">
<CardBody>
<div className="text-center mt-3">
<h3 className="font-weight-bold"><a href=""
className="text-dark text-uppercase account-pages-logo">Sign In</a>
</h3>
<u><p className="text-muted">Enter your credentials to continue to the platform.</p></u>
</div>
<div className="p-3">
<AvForm onSubmit={this.handleSubmit}>
<FormGroup>
<Label htmlFor="username">Email</Label>
<AvField type="text" name="username" value={this.state.email}
onChange={this.handleFieldChange} required className="form-control"
id="username"
placeholder="Enter email" />
</FormGroup>
<FormGroup>
<Label htmlFor="userpassword">Password</Label>
<AvField type="password" name="password" value={this.state.password}
onChange={this.handleFieldChange} required className="form-control"
id="userpassword" placeholder="Enter password" />
</FormGroup>
<div className="custom-control custom-checkbox">
<Input type="checkbox" className="custom-control-input" id="customControlInline" />
<Label className="custom-control-label" htmlFor="customControlInline">Remember
me</Label>
</div>
<div className="mt-3">
<Button color="none" type="submit" className="sign-in-button" >Sign In</Button>
</div>
<div className="mt-4 mb-0 text-center">
<Link to="password_forget" className="text-dark"><i className="mdi mdi-lock"></i> Forgot
your password?</Link>
</div>
</AvForm>
</div>
</CardBody>
</Card>
</Col>
</Row>
</Container>
</div>
</div>
</section>
</React.Fragment>
);
}
}
Login.PropTypes = {
dispatch: PropTypes.func,
login: PropTypes.func
};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
login
}, dispatch)
};
}
const mapStateToProps = (state) => {
const { isLoggedIn } = state.auth;
const { message } = state.message;
return {
isLoggedIn,
message
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
And I made so many changes and I can't fix this:
enter image description here
I am trying to push the login details, fetched from the bckend to the Session Storage and push it to the Redux so I can fetch the data later after loging and keep the token, id, password and email for the user
Somewhere in the documentation I have read that if we use mapDispatchToProps function in the connect method then the component will not get dispatch function as props .I tried finding the document link but could not get it.
try debugging component props to see dispatch function is there or not
You are already binding login with the dispatch.
So to call that, you need to do this;
this.props.login(...)
instead of this;
dispatch(login(...))
Functions in mapDispatchToProps are added to dispatch and if you call them like this this.props.function_name(), they are dispatched too.

When login button is clicked the page is Redirecting but not rendering the page

In user login form I am when I click login button it gives a token.
I am setting up the token in local storage and redirecting to 'customers' page.The page gets redirected to 'customers' but the page is not rendered.I have added a console.log in customer Page to check the whether the page is rendering or not.In this case it is not rendering it.
LoginForm
import React from 'react';
import _ from 'lodash';
import axios from '../config/Axios';
class LoginForm extends React.Component {
constructor() {
super()
this.state = {
email: '',
password: '',
error: '',
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
})
}
handleSubmit(e) {
e.preventDefault()
const loginData = {
email: this.state.email,
password: this.state.password,
}
axios.post('/users/login', loginData, {
headers: {
'x-auth': localStorage.getItem('token')
}
})
.then((response) => {
console.log(response.data)
if (response.data.error) {
const error = response.data.error
this.setState({ error })
}
else {
const token = response.data.token
localStorage.setItem('token', token);
this.props.history.push('/customers')
}
})
.catch(err => console.log(err))
}
render() {
return (
<div className="form-group container">
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<br />
<label htmlFor="email-login">Email</label>
<br />
<input type="email" value={this.state.email} onChange={this.handleChange} name="email" id="email-login" />
<br />
<label htmlFor="password-login">Password</label>
<br />
<input type="password" value={this.state.password} onChange={this.handleChange} name="password" id="password-login" />
<br />
<button type="submit" className="btn btn-info">Login</button>
{
!_.isEmpty(this.state.error) && <h3>{this.state.error}</h3>
}
</form>
</div>
)
}
}
export default LoginForm
Customer Page
class CustomerList extends React.Component {
constructor() {
super()
this.state = {
customers: [],
isLoading: true,
}
this.handleChange = this.handleChange.bind(this)
this.handleSave = this.handleSave.bind(this)
}
componentDidMount() {
axios.get('/customers', {
headers: {
'x-auth': localStorage.getItem('token')
}
})
.then(response => {
console.log(response.data)
const customers = response.data
this.setState({ customers, isLoading: false })
return customers
})
.catch(err => console.log(err))
}
handleChange(text) {
console.log(this.state.customers)
const customerDatas = this.state.customers
const customers = customerDatas.filter((customerData => {
return customerData.name.toLowerCase().includes(text)
}))
if (this.state.customers.length) {
this.setState({ customers })
}
else {
console.log(`${customers}......`)
this.componentDidMount()
}
}
render() {
console.log('customer localStorage', localStorage)
return (
<div>
<br />
<SearchBox handleChange={this.handleChange} />
<br />
{this.state.isLoading ? (
<Loader />
) : (
<div>
<h3>Progress Bar</h3>
</div>
)}
<br />
</div>
)
}
}
export default CustomerList
As discussed in chat and comments - you did not have router added in your Router Component
Please add the below line
<Route path='/customers' exact={true} component={CustomerList} />
check what the this.props what will be shown is that content history or not undefined
and do you have a router to element

Reset Password in React

i am creating a mern app. i got stuck in forgot password. i'm able to send a mail for forgot password but when i try to set new password it is not changing password but in postman i was able to change the password but when it comes to react i was not. I know the problem is that i was not able to get token as params .
work in postman but not in when i try in react.
Resetpassword component
import React, { Fragment, useState } from 'react';
import { connect } from 'react-redux';
import { Link, Redirect } from 'react-router-dom';
import { setAlert } from '../../actions/alert';
import { reset } from '../../actions/auth';
import PropTypes from 'prop-types';
const Reset = ({ setAlert, reset }) => {
const [formData, setFormData] = useState({
password: '',
password2: ''
});
const { password, password2 } = formData;
const onChange = e =>
setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = async => {
const token = props.match.params.token;
console.log(token);
if (password !== password2) {
setAlert('password does not matched', 'danger');
} else {
reset({ password, token });
}
};
return (
<Fragment>
<section className='container'>
<h1 className='large text-primary'>RESET PASSWORD</h1>
<p className='lead'>
<i className='fas fa-user' /> Create Your NEW PASSWORD
</p>
<form
className='form'
onSubmit={e => onSubmit(e)}
action='create-profile.html'
>
<div className='form-group'>
<input
type='password'
placeholder='Password'
name='password'
value={password}
onChange={e => onChange(e)}
/>
</div>
<div className='form-group'>
<input
type='password'
placeholder='Confirm Password'
name='password2'
value={password2}
onChange={e => onChange(e)}
/>
</div>
<input type='submit' className='btn btn-primary' value='Register' />
</form>
<p className='my-1'>
Already have an account? <Link to='/login'>Sign In</Link>
</p>
</section>
</Fragment>
);
};
Reset.propTypes = {
setAlert: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired
};
export default connect(
null,
{ setAlert, reset }
)(Reset);
resetaction.JS
export const reset = ({ password, token }) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ password, token });
try {
const res = await axios.put(
`http://localhost:3000/api/auth/reset/${token}`,
body,
config
);
dispatch({
type: RESET_PASSWORD,
payload: res.data
});
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
}
}
};
By only seeing this snippet I assume your problems are following lines:
const Reset = ({ setAlert, reset }) => {
//...
const token = props.match.params.token;
You destructed the whole props argument (into { setAlert, reset }), so in your case props is undefined. You should adapt your code to this:
const Reset = ({ setAlert, reset, match }) => {
//...
const token = match.params.tok

how to show a new user on the page?

I'm a Junior. I could not find information on the Internet and could not solve it myself. I use for my app the react with axios. Through the post request I want to add a new user. How to show a new user on the page? not in the console.
import React from "react"; import axios from "axios";
class PersonInput extends React.Component {
state = {
name: '',
}
handleChange = (event) => {
this.setState({name: event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
const user = {
name: this.state.name
};
axios.post(`https://jsonplaceholder.typicode.com/users`, {user}).then((res) => {
console.log(res);
})
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
User Name:
<input type="text" name="name" onChange={this.handleChange} />
</label>
<button type="submit">Add user</button>
<ul>{this.state.res}</ul>
</form>
</div>
);
}
}
export default PersonInput;
You didn't need to make a post request for it.
Try this:
import React from "react"; import axios from "axios";
class PersonInput extends React.Component {
state = {
name: '',
users: ''
}
handleChange = (event) => {
this.setState({name: event.target.value});
}
handleSubmit = (event) => {
event.preventDefault();
this.setState({users: this.state.name});
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
User Name:
<input type="text" name="name" onChange={this.handleChange} />
</label>
<button type="submit">Add user</button>
<ul>{this.state.users}</ul>
</form>
</div>
);
}
}
export default PersonInput;
Define a new key to your state and when u get response store it in that key
state = {
name: '',
res: []
}
onSubmit = async e => {
e.preventDefault();
const data = { name: this.state.name };
const response = await axios.post('https://jsonplaceholder.typicode.com/users', data);
this.setState({res: response.data});
}
And use async await it is easier and cleaner. Just a personal preference

React JS Prop function undefined on Proptypes

I want to call a function/action when the form is submited but when the submit trigger React puts
TypeError: this.props.login(...) is undefined
This is the Login.js Page:
import React from 'react';
import PropTypes from 'prop-types';
import Empty from '../Layouts/Empty';
import { Button } from '../UI/Button';
import axios from 'axios'
import Notifications, {notify} from 'react-notify-toast';
import { connect } from 'react-redux'
import { login } from '../../actions/login'
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
isLoading: false
};
this.onChange = this.onChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
handleSubmit(event) {
event.preventDefault();
this.setState({ isLoading: true });
this.props.login(this.state).then(function(response) {
if (response.status == 404){
return response.json().then(function(val) {
let myColor = { background: '#f44242', text: "#FFFFFF" };
notify.show(val.message, "custom", 5000, myColor);
});
}else if (response.status == 200){
return response.json().then(function(val) {
let myColor = { background: '#3AB54A', text: "#FFFFFF" };
notify.show("Welcome!", "custom", 5000, myColor);
})
}else if (response.status == 422){
return response.json().then(function(val) {
let myColor = { background: '#f44242', text: "#FFFFFF" };
notify.show("The email/password field is required", "custom", 5000, myColor);
})
}
}).catch(function() {
let myColor = { background: '#f44242', text: "#FFFFFF" };
notify.show("Error en comunicacion" , "custom", 5000, myColor);
});
}
render() {
const {email, password, isLoading} = this.state;
return (
<Empty>
<div className='main'>
<Notifications />
</div>
<h1>Welcome Again</h1>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label>Email</label>
<input type="email" placeholder="email#domain.com" name="email" value={email} className="form-control" onChange={this.onChange}/>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" placeholder="***********" name="password" value={password} className="form-control" onChange={this.onChange} />
</div>
<div className="form-group form-group-button">
<button type="submit" className="button button-primary button-right button" style={{width: "100%"}} disabled={isLoading}>LOGIN</button>
</div>
</form>
<div className="form-description">
<Button to='#' classes="button-block button-google">Login using Google+</Button>
<Button to='#' classes="button-block button-facebook">Login using Facebook</Button>
</div>
</Empty>
);
}
}
LoginForm.propTypes = {
dispatch: PropTypes.func,
login: PropTypes.func.isRequired
};
export default connect(null, { login })(LoginForm);
The action on the folder actions is:
export function login(data) {
return dispatch => {
fetch('MY_URL', {
method: 'post',
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
body: data
});
}
}
And the error when the submit trigger:
TypeError: this.props.login(...) is undefined
handleSubmit
src/components/Pages/Login.js:30
27 | handleSubmit(event) {
28 | event.preventDefault();
29 | this.setState({ isLoading: true });
> 30 | this.props.login(this.state).then(function(response) {
31 | if (response.status == 404){
32 | return response.json().then(function(val) {
33 | let myColor = { background: '#f44242', text: "#FFFFFF" };
What am i missing?
Regards.
You misused connect with the second argument: http://redux.js.org/docs/basics/UsageWithReact.html#implementing-container-components
You should have done this way
const mapDispatchToProps = dispatch => {
return {
login: data => dispatch(login(data)),
}
}
export default connect(null, mapDispatchToProps)(LoginForm);

Resources