My this.props are undefined on handleSubmit and other function - reactjs

I do not know why my this.props are showing undefined on handleSubmit. I have done everything possible. When I log state in mapStateToProps, I see what the action has dispatched, but this.props is not taking it.
This is what I get (https://imgur.com/a/AXixWn9) in logger when I enter the wrong details
I have searched online but no clue. I have debugged this for hours
Login.js
import React from 'react';
import { NavLink } from 'react-router-dom';
import { Redirect } from 'react-router';
import qs from 'qs';
import { connect } from 'react-redux';
import Outer from '../templates/outer';
import history from '../../_helpers/history';
import routes from '../../services/urls';
import apiRequest from '../../services/api';
import loginUser from '../../redux/actions/authentication.action';
import { LOGIN_FAILURE, LOGIN_SUCCESS } from '../../redux/constants/authentication.constants';
import alertActions from '../../redux/actions/alert.actions';
class LoginComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: {},
errors: {},
loginError: '',
submitted: false,
};
const { dispatch } = this.props;
dispatch(alertActions.clear());
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(field, e) {
const { fields } = this.state;
fields[field] = e.target.value;
this.setState({ fields });
}
handleValidation() {
const { fields } = this.state;
const errors = {};
let formIsValid = true;
// Login code
if (!fields.code) {
formIsValid = false;
errors.code = 'This field is required';
}
// Password
if (!fields.password) {
formIsValid = false;
errors.password = 'This field is required';
}
this.setState({ errors });
return formIsValid;
}
handleSubmit(e) {
e.preventDefault();
const { code, password } = this.state.fields;
this.setState({ submitted: true });
if (this.handleValidation()) {
this.setState({ submitted: true });
const payload = {
email: code,
// level: 'SYSTEM',
password,
};
const { dispatch } = this.props;
dispatch(loginUser(payload));
} else {
this.setState({ submitted: false });
}
}
render() {
const { loginError, submitted } = this.state;
return (
<Outer>
<form onSubmit={this.handleSubmit}>
<div className="mt-5" />
{loginError
&& <div className="alert alert-danger">{loginError}</div>
}
<label>Login Code</label>
<input type="text" className="form-control" onChange={this.handleChange.bind(this, 'code')} value={this.state.fields.code || ''} />
<div className="text-danger mt-1 mb-1">{this.state.errors.code}</div>
<br />
<label>Password</label>
<input type="password" className="form-control" onChange={this.handleChange.bind(this, 'password')} value={this.state.fields.password || ''} />
<div className="text-danger mt-1 mb-1">{this.state.errors.password}</div>
<br />
<button
className="btn btn-primary btn-block text-uppercase"
type="submit"
disabled={submitted}
>
{ !submitted ? 'Login to manage my cards' : 'loading...' }
</button>
<div className="row no-gutters mt-4">
<div className="col-md-12">
<NavLink to="/reset-password" className="grey">I have forgotten my password</NavLink>
</div>
</div>
</form>
</Outer>
);
}
}
function mapStateToProps(state) {
console.error(state);
const { auth } = state.authentication;
const { alert } = state.alert;
return { auth, alert };
}
export default connect(mapStateToProps)(LoginComponent);
store.js
export const store = createStore(
rootReducer,
applyMiddleware(
thunkMiddleware,
loggerMiddleware,
),
);
authentication.reducer.js
function loginReducer(state = {}, action) {
switch (action.type) {
case LOGIN_REQUEST:
return {
user_status: LOGIN_REQUEST,
user_data: action,
};
case LOGIN_SUCCESS:
return {
user_status: LOGIN_SUCCESS,
user_data: action,
};
case LOGIN_FAILURE:
return {
user_status: LOGIN_FAILURE,
user_data: action,
};
default:
return state;
}
}
export default loginReducer;
authentication.action.js
function loginUser(payload) {
function request(user) { return { type: LOGIN_REQUEST, user }; }
function success(response) { return { type: LOGIN_SUCCESS, response }; }
function failure(error) { return { type: LOGIN_FAILURE, error }; }
return (dispatch) => {
// const request = apiRequest(routes.LOGIN, 'POST', qs.stringify(payload));
// const ah = loginUser(qs.stringify(payload));
// console.log(ah);
dispatch(request(payload));
const fetch = apiRequest(routes.LOGIN, 'POST', qs.stringify(payload));
return fetch.then((response) => {
switch (response.status) {
case 400:
dispatch(failure(response));
dispatch(alertActions.error(response.data.description));
break;
case 200:
if (response.data.code === 0) {
localStorage.setItem('qwplx44', JSON.stringify(response.data));
dispatch(success(response.data));
history.push('/dashboard');
break;
}
dispatch(failure(response.data.description));
dispatch(alertActions.error(response.data.description));
break;
default:
return {};
}
}).catch((error) => {
dispatch(failure(error.response.data.message));
dispatch(alertActions.error(error.response.data.message.toString()));
// return false;
});
root-reducer
const rootReducer = combineReducers({
authentication: loginReducer,
alert: alertReducer,
});
export default rootReducer;

You aren't connecting your component correctly. You are trying to destruct keys out of the state that aren't there. I don't see a key called auth on your loginReducer. So this line const { auth } = state.authentication; would return undefined. That is why when logging props auth is undefined.
Instead just pluck what you want from state and you can alias authentication as auth while destructuring :)
const mapStateToProps = ({authentication: auth, alert}) => ({
auth,
alert
})
If you are trying to use the data stored in your reducer after making the request, then you should use the componentDidUpdate lifecycle method
componentDidUpdate(prevProps) {
const { auth: prevAuth } = prevProps
const { auth } = this.props
if (auth.user_status && ((!prevAuth.user_status && auth.user_status) || prevAuth.user_status !== auth.user_status)) {
// TODO handle response from api here
/* ex
if (auth.user_status === LOGIN_FAILURE) {
this.setState({loginFailure: auth.user_data})
}
*/
}
}
Few other things.
You aren't binding handleValidation to the class, but are trying to access state.
You are calling this.setState({ submitted: true }); twice in handleSubmit. Second one is redundant and not needed.
I would refactor your apiRequest to handle the qs.stringify and the error / auth / response status handling, so you dont have to write out that same stuff for every api call :)

Related

Redux: My state is receiving '##redux/INITi.8.g.w.a.m' Redux state not updating

I've been debugging why my state hasn't been changing and noticed this being logged in my reducer:
{ type: '##redux/INITi.8.g.w.a.m' }
This is the store which includes state, action types, reducer, actions:
/* initial state */
import axios from 'axios';
export var usersStartState = {
accountNotVerified: null,
isLoggedIn: false,
error: true,
userAvatar: 'uploads/avatar/placeholder.jpg'
};
/* action types */
export const actionTypes = {
RESET_USER_ACCOUNT_IS_VERIFIED: 'RESET_USER_ACCOUNT_IS_VERIFIED',
USER_ACCOUNT_IS_VERIFIED: 'USER_ACCOUNT_IS_VERIFIED',
USER_ACCOUNT_NOT_VERIFIED: 'USER_ACCOUNT_NOT_VERIFIED',
IS_LOGGED_IN: 'IS_LOGGED_IN',
IS_LOGGED_OUT: 'IS_LOGGED_OUT',
LOAD_USER_AVATAR: 'LOAD_USER_AVATAR',
ERROR_LOADING: 'ERROR_LOADING' // LOAD_MULTER_IMAGE: "LOAD_MULTER_IMAGE"
};
/* reducer(s) */
export default function users(state = usersStartState, action) {
console.log('In users reducer! ', action);
switch (action.type) {
case actionTypes.RESET_USER_ACCOUNT_IS_VERIFIED:
return Object.assign({}, state, { accountNotVerified: null });
case actionTypes.USER_ACCOUNT_IS_VERIFIED:
return Object.assign({}, state, { accountNotVerified: false });
case actionTypes.USER_ACCOUNT_NOT_VERIFIED:
return Object.assign({}, state, { accountNotVerified: true });
case actionTypes.IS_LOGGED_IN:
return Object.assign({}, state, { isLoggedIn: true });
case actionTypes.IS_LOGGED_OUT:
return Object.assign({}, state, { isLoggedIn: false });
case actionTypes.LOAD_USER_AVATAR:
return { ...state, userAvatar: action.data };
case actionTypes.ERROR_LOADING:
return Object.assign({}, state, { error: true });
default:
return state;
}
}
/* actions */
export const resetUserAcoountVerified = () => {
return { type: actionTypes.RESET_USER_ACCOUNT_IS_VERIFIED };
};
export const userHasBeenVerified = () => {
return { type: actionTypes.USER_ACCOUNT_IS_VERIFIED };
};
export const userHasNotBeenVerified = () => {
return { type: actionTypes.USER_ACCOUNT_NOT_VERIFIED };
};
export const logInUser = () => {
return { type: actionTypes.IS_LOGGED_IN };
};
export const logOutUser = () => {
axios
.get('/users/logout')
.then(response => {
if (response.status === 200) {
console.log('You have been logged out!');
}
})
.catch(function(error) {
if (error.response.status === 500) {
console.log('An error has occured');
}
});
return { type: actionTypes.IS_LOGGED_OUT };
};
export const loadAvatar = data => {
console.log('in load avatar ', data);
return { type: actionTypes.LOAD_USER_AVATAR, data: data };
};
export const errorLoading = () => {
return { type: actionTypes.ERROR_LOADING };
};
And this is my component:
import { useState } from 'react';
import { Card, Icon, Image, Segment, Form } from 'semantic-ui-react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { loadAvatar } from '../../store/reducers/users/index';
import axios from 'axios';
function ImageUploader({ userAvatar }) {
var [localUserAvatar, setLocalUserAvatar] = useState(userAvatar);
function fileUploader(e) {
e.persist();
var imageFormObj = new FormData();
imageFormObj.append('imageName', 'multer-image-' + Date.now());
imageFormObj.append('imageData', e.target.files[0]);
loadAvatar('foo');
axios({
method: 'post',
url: `/users/uploadmulter`,
data: imageFormObj,
config: { headers: { 'Content-Type': 'multipart/form-data' } }
})
.then(data => {
if (data.status === 200) {
console.log('data ', data);
console.log('path typeof ', typeof data.data.path);
loadAvatar('foo');
setLocalUserAvatar('../../' + data.data.path);
}
})
.catch(err => {
alert('Error while uploading image using multer');
});
}
Here are
console.log('userAvatar in imageUploader ', userAvatar);
console.log('Date.now() line 44 in imageUploader ', Date.now());
console.log('localUserAvatar in imageUploader ', localUserAvatar);
console.log('Date.now() line 46 in imageUploader ', Date.now());
console.log("loadAvatar('barbar') ", loadAvatar('barbar'));
return (
<>
<Segment>
<Card fluid>
<Image src={localUserAvatar} alt="upload-image" />
<Segment>
<Form encType="multipart/form-data">
<Form.Field>
<input
placeholder="Name of image"
className="process__upload-btn"
type="file"
content="Edit your Avatar!"
onChange={e => fileUploader(e)}
/>
</Form.Field>
</Form>
</Segment>
<Card.Content>
<Card.Header>Charly</Card.Header>
<Card.Meta>
<span className="date">Joined in 2015</span>
</Card.Meta>
<Card.Description>Charly</Card.Description>
</Card.Content>
<Card.Content extra>
<a>
<Icon name="user" />
22 Friends
</a>
</Card.Content>
</Card>
</Segment>
</>
);
}
function mapStateToProps(state) {
const { users } = state;
const { userAvatar } = users;
return { userAvatar };
}
const mapDispatchToProps = dispatch => bindActionCreators({ loadAvatar }, dispatch);
export default connect(
mapStateToProps,
mapDispatchToProps
)(ImageUploader);
From the logs you can see loadAvatar the dispatcher, gets fired in the component and in the store...
But the state in the store never changes....
Also other states do change correctly...Like for example I have a Modal and that updates nicely.
Any help would be appreciated as what { type: '##redux/INITi.8.g.w.a.m' } and why my state is not updating?
Redux dispatches that action as an internal initialization step:
// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
dispatch({ type: ActionTypes.INIT })
It's specifically so that your reducers will see the action, not recognize the action type, and return their default state, thus defining the initial overall app state contents.

Why is the state change not making it's way to the reducer?

I have a file called Login.js in the file I call this.props.onUserLogin and pass the users auth token. I can verify that I am getting the token from the api call via the console log. When I get to the reducer I loose the value from the state somehow. I can verify that with the console log as well. I am trying to figure out why my state.loginToken is empty in the reducer.
Login.js
import React, {Component} from 'react';
import * as actionTypes from '../Store/actions';
import UserInfoButton from '../UserInfoButton/UserInfoButton';
import { connect } from 'react-redux';
const axios = require('axios');
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
};
}
handleChange = (event) => {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
})
}
handleLogin = (event) => {
axios.post('http://127.0.0.1:8000/api/user/token/', {
email: this.state.email,
password: this.state.password,
}).then( (response) => {
console.log('response: ' + response.data.token);
this.props.onUserLogin(response.data.token);
})
.catch( (error) => {
console.log(error)
})
event.preventDefault();
}
render() {
const isLoggedIn = this.props.loginToken;
let userButton;
if(isLoggedIn) {
userButton = <UserInfoButton/>
} else {
userButton = "";
}
return (
<div>
<form onSubmit={this.handleLogin}>
Email:<br/>
<input type="text" value={this.state.email} onChange={this.handleChange} name="email"/><br/>
Password:<br/>
<input type="password" value={this.state.password} onChange={this.handleChange} name="password"/><br/>
<br/>
<input type="submit" value="Submit"></input>
</form>
<div>{this.props.loginToken}</div>
<br/>
<div>{userButton}</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
loginToken: state.loginToken
};
}
const mapDispatchToProps = dispatch => {
return {
onUserLogin: (userToken) => dispatch( { type: actionTypes.USER_LOGIN_ACTION, loginToken: userToken } ),
};
}
export default connect(mapStateToProps,mapDispatchToProps)(Login);
reducer.js
import * as actionTypes from './actions';
const initialState = {
loginToken: '',
}
const reducer = (state = initialState, action) => {
switch(action.type) {
case actionTypes.USER_LOGIN_ACTION:
console.log('action: ' + state.loginToken);
return {
...state,
loginToken: state.loginToken,
}
default:
return state;
}
};
export default reducer;
Basically a typo inside of your reducer - You're looking for the loginToken inside of state instead of the action.
case actionTypes.USER_LOGIN_ACTION:
console.log('action: ' + action.loginToken); // not state.loginToken
return {
...state,
loginToken: action.loginToken, // not state.loginToken
}

reactjs payload shows undefined

I am kind of learning how react js and its workflows such as redux work out.. In some cases I have no idea why I can not get response from API such as below my code when I request data I can nott get payload it is undefined any suggestion ? and may I know what cause it is show that payload is undefined.
RegisterAction.js
import { REGISTER_REQUEST, REGISTER_SUCCESS, REGISTER_FAILURE } from './types';
import { authService } from '../services/authService';
export function register(user) {
return dispatch => {
dispatch(request(user));
authService.register(user)
.then(
user => { dispatch(success()); },
error => { dispatch(failure(error)); }
);
};
function request(user) {
return { type: REGISTER_REQUEST, payload: user }
}
function success(user) {
return { type: REGISTER_SUCCESS, payload: user }
}
function failure(error) {
return { type: REGISTER_FAILURE, error }
}
}
RegistrationReducer.js
import { REGISTER_REQUEST, REGISTER_SUCCESS, REGISTER_FAILURE } from '../actions/types';
const initialState = {
user: [],
registerigIn: false,
registeredIn: false
}
export default function (state = initialState, action) {
console.info("reducer:", "state is:", state, "action is:", action.payload)
switch (action.type) {
case REGISTER_REQUEST:
return {
...state,
user: action.payload,
registering: true,
};
case REGISTER_SUCCESS:
return {
...state,
user: action.payload,
registered: true
};
case REGISTER_FAILURE:
return {
registering: false
};
default:
return state
}
}
AuthService.js
import { authHeader } from '../helpers/authHeader';
export const authService = {
register
};
function register(user) {
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
};
return fetch(`https://reqres.in/api/register`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
if (response.status === 401) {
// auto logout if 401 response returned from api
logout();
// location.reload(true);
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}
View
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { register } from '../../actions/registrationActions'
import './Authentication.css'
class Registration extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: ''
}
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this)
}
onChange(e) {
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit(e) {
e.preventDefault();
const user = this.state;
console.info("register Data:", user)
this.props.register(user)
}
render() {
return (
<React.Fragment>
< div className="text-center" >
<div className="logo">Register Page</div>
<div className="login-form-1">
<form id="register-form" className="text-left" onSubmit={this.onSubmit}>
<div className="login-form-main-message"></div>
<div className="main-login-form">
<div className="login-group">
<div className="form-group">
<label htmlFor="reg_surname" className="sr-only">Email</label>
<input
type="text"
className="form-control"
id="email"
name="email"
onChange={this.onChange}
placeholder="email" />
</div>
<div className="form-group">
<label htmlFor="reg_password" className="sr-only">Password</label>
<input type="password"
className="form-control"
id="password"
name="password"
onChange={this.onChange}
placeholder="" />
</div>
</div>
<button
type="submit"
title="Gönder"
className="login-button"><i className="fa fa-chevron-right"></i>
</button>
</div>
<div className="etc-login-form">
<p>Hesabiniz var mı? Giriş yap</p>
</div>
</form>
</div>
</div >
</React.Fragment>
)
}
}
Registration.propTypes = {
register: PropTypes.func.isRequired
}
export default connect(null, { register })(Registration)

Cannot read property success of undefined using Reactjs Toastr

Am working with react toastr from this link https://www.npmjs.com/package/react-toastr for my react-redux form.
i have installed as npm install --save react-toastr and I have made import as per requirements
import { ToastContainer } from "react-toastr";
import { ToastMessage } from "react-toastr";
import { ToastMessageAnimated } from "react-toastr";
let container;
at src/index.html i have toastr.min.css and toastr.animate.css files
At RegisterPage Component when I click at Test Toastr Button as per code below, everything works fine
<div className="container">
<ToastContainer
ref={ref => container = ref}
className="toast-top-right"
/>
<h1>
React-Toastr
<small>React.js toastr component</small>
</h1>
<div className="btn-container">
<button
className="primary"
onClick={() =>
container.success(`hi! Now is `, `success`, {
closeButton: true,})
}
>
Test Toastr
</button>
</div>
</div>
Here is what am trying to do.
I want to display toastr message alert once form registeration is successful but it shows error
bundle.js:36689 Uncaught TypeError: Cannot read property 'success' of undefined when I added this code below at user.service.js files
setTimeout(()=>{ this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,}); }, 400);
Here is the user.service.js files
import config from 'config';
import { authHeader } from '../_helpers';
import { ToastContainer } from "react-toastr";
import { ToastMessage } from "react-toastr";
import { ToastMessageAnimated } from "react-toastr";
let container;
export const userService = {
register,
update,
};
function register(user) {
const requestOptions = {
method: 'POST',
return fetch(`../register.php`, requestOptions).then(handleResponse)
.then(res => {
if (res) {
//setTimeout(()=>{ this.container.success('Data added successfully'); }, 400);
setTimeout(()=>{ this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,}); }, 400);
console.log('Data added suucessfully');
}
return user;
});
}
The code below is the RegisterPage Component.
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { userActions } from '../_actions';
import { ToastContainer } from "react-toastr";
import { ToastMessage } from "react-toastr";
import { ToastMessageAnimated } from "react-toastr";
let container;
class RegisterPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
firstName: '',
lastName: '',
username: '',
password: ''
},
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const { name, value } = event.target;
const { user } = this.state;
this.setState({
user: {
...user,
[name]: value
}
});
}
handleSubmit(event) {
event.preventDefault();
this.setState({ submitted: true });
const { user } = this.state;
const { dispatch } = this.props;
if (user.firstName && user.lastName && user.username && user.password) {
dispatch(userActions.register(user));
}
}
render() {
const { registering } = this.props;
const { user, submitted } = this.state;
return (
<div className="col-md-6 col-md-offset-3">
<h2>Test Toastr</h2>
<div className="container">
<ToastContainer
ref={ref => container = ref}
className="toast-top-right"
/>
<h1>
React-Toastr
<small>React.js toastr component</small>
</h1>
<div className="btn-container">
<button
className="primary"
onClick={() =>
container.success(`hi! Jmarkatti `, `success`, {
closeButton: true,})
}
>
Test Toastr
</button>
</div>
</div>
<form name="form" onSubmit={this.handleSubmit}>
// form input removed to reduce code
<div className="form-group">
<button className="btn btn-primary">Register</button>
{registering &&
}
</div>
</form>
</div>
);
}
}
function mapStateToProps(state) {
const { registering } = state.registration;
return {
registering
};
}
const connectedRegisterPage = connect(mapStateToProps)(RegisterPage);
export { connectedRegisterPage as RegisterPage };
An Updates is Here
user.action.js code
function register(user) {
return dispatch => {
dispatch(request(user));
userService.register(user)
.then(
user => {
/*
dispatch(success());
history.push('/login');
dispatch(alertActions.success('Registration successful'));
*/
},
error => {
dispatch(failure(error.toString()));
dispatch(alertActions.error(error.toString()));
}
);
};
user.reducer.js codes
import { userConstants } from '../_constants';
export function registration(state = {}, action) {
switch (action.type) {
case userConstants.REGISTER_REQUEST:
return { registering: true };
case userConstants.REGISTER_SUCCESS:
return {};
case userConstants.REGISTER_FAILURE:
return {};
default:
return state
}
}
Try with this
Change
setTimeout(()=>{ this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,}); }, 400);
To
setTimeout(()=>{
if(this.container){
this.container.success(`hi! jmarkatti `, `success`, {closeButton: true,});
}
}, 400);

How to pass my error message from server called by axios to my component

I'm really new to React. I have an axios request in my actions I want my error message to pass on the component I have this code :
import axios from 'axios';
import setAuthorizationToken from '../utils/setAuthorizationToken';
import jwtDecode from 'jwt-decode';
import { SET_CURRENT_USER, BASE_URL } from './types';
const instance = axios.create({
baseURL: BASE_URL
});
export function setCurrentUser(user) {
return {
type: SET_CURRENT_USER,
user
};
}
export function logout() {
return dispatch => {
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
setAuthorizationToken(false);
dispatch(setCurrentUser({}));
}
}
export function login(data) {
return dispatch => {
return instance.post('/authenticate', data).then(function(response) {
const token = response.data.accessToken;
const refreshToken = response.data.refreshToken;
localStorage.setItem('accessToken', token);
localStorage.setItem('refreshToken', refreshToken);
setAuthorizationToken(token);
dispatch(setCurrentUser(jwtDecode(token)));
})
.catch(function(error){
console.log('error: ', error.response.data);
});
}
}
Here is my Component:
import React from 'react';
import TextFieldGroup from '../common/TextFieldGroup';
import validateInput from '../../server/validations/login';
import { connect } from 'react-redux';
import { login } from '../../actions/authActions';
class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
errors: {},
isLoading: false
};
this.onSubmit = this.onSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
isValid() {
const { errors, isValid } = validateInput(this.state);
if (!isValid) {
this.setState({ errors });
}
return isValid;
}
onSubmit(e) {
e.preventDefault();
if (this.isValid()) {
this.setState({ errors: {}, isLoading: true });
this.props.login(this.state).then(
(res) => this.context.router.push('/'),
(error) => this.setState({ errors: error.response.data , isLoading: false }),
);
}
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
render() {
const { errors, username, password, isLoading } = this.state;
return (
<form onSubmit={this.onSubmit}>
<h1>Login</h1>
{ errors.message && <div className="alert alert-danger">{errors.message}</div> }
<TextFieldGroup
field="username"
label="Username"
value={username}
error={errors.username}
onChange={this.onChange}
/>
<TextFieldGroup
field="password"
label="Password"
value={password}
error={errors.password}
onChange={this.onChange}
type="password"
/>
<div className="form-group"><button className="btn btn-primary btn-lg" disabled={isLoading}>Login</button></div>
</form>
);
}
}
LoginForm.propTypes = {
login: React.PropTypes.func.isRequired
}
LoginForm.contextTypes = {
router: React.PropTypes.object.isRequired
}
export default connect(null, { login })(LoginForm);
Here is the console.log
error: Object {code: "UNAUTHORIZED", message: "Invalid username or password."}
Currently I don't know to pass my error message to component. I'm really new to React and Redux
First you have to add the initial state on reducer. For example
authReducer.js
const initialState = {
... // another state
errors: {}
}
function yourReducer(state = initialState, action) {
case 'SHOW_ERROR':
return {
...state,
errors: action.message
}
default:
return state
}
On login action dispatch the 'SHOW_ERROR'
authActions.js
export function login(data) {
return dispatch => {
return instance.post('/authenticate', data).then(function(response) {
...
// success
})
.catch(function(error){
// fail
dispatch({ type: 'SHOW_ERROR', message: error.response.data })
});
}
}
Then you need to map redux state to be a props on your component
LoginComponent.js
function mapStateToProps(state) {
return {
you: may.return.another.state.here,
errors: state.yourReducerName.errors
}
}
export default connect(mapStateToProps, { login })(LoginForm);
Finally, you can call errors as a props on your Component
class LoginForm extends React.Component {
...
render() {
const { errors, username, password, isLoading } = this.state;
const { errors } = this.props // errors from redux state
return (
<form onSubmit={this.onSubmit}>
<p>{errors.message}</p>
<h1>Login</h1>
...
<div className="form-group"><button className="btn btn-primary btn-lg" disabled={isLoading}>Login</button></div>
</form>
);
}
}
Don't forget to validate the prop types. Good luck!

Resources