Losing JWT token after browser refresh - reactjs

I'm learning Full Stack Development with Spring Boot 2.0 and React .
The authentication and authorization are managed by JWT and the app works as expected except I have to re-login after I refresh the browser.
How to maintain JWT token even after browser refresh ?
import React, { Component } from 'react';
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import Snackbar from '#material-ui/core/Snackbar';
import Carlist from './Carlist';
import {SERVER_URL} from '../constants.js';
class Login extends Component {
constructor(props) {
super(props);
this.state = {username: '', password: '', isAuthenticated: false, open: false};
}
logout = () => {
sessionStorage.removeItem("jwt");
this.setState({isAuthenticated: false});
}
login = () => {
const user = {username: this.state.username, password: this.state.password};
fetch(SERVER_URL + 'login', {
method: 'POST',
body: JSON.stringify(user)
})
.then(res => {
const jwtToken = res.headers.get('Authorization');
if (jwtToken !== null) {
sessionStorage.setItem("jwt", jwtToken);
this.setState({isAuthenticated: true});
}
else {
this.setState({open: true}); // maintient snackbar ouvert
}
})
.catch(err => console.error(err))
}
handleChange = (event) => {
this.setState({[event.target.name] : event.target.value});
}
handleClose = (event) => {
this.setState({ open: false });
}
render() {
if (this.state.isAuthenticated === true) {
return (<Carlist />)
}
else {
return (
<div>
<br/>
<TextField tpye="text" name="username" placeholder="Username"
onChange={this.handleChange} /><br/>
<TextField type="password" name="password" placeholder="Password"
onChange={this.handleChange} /><br /><br/>
<Button variant="raised" color="primary" onClick={this.login}>Login</Button>
<Snackbar
open={this.state.open} onClose={this.handleClose}
autoHideDuration={1500} message='Check your username and password' />
</div>
);
}
}
}
export default Login;

I think you simply don't check for the token in local storage in your constructor. When you reload the page, your constructor executes and sets isAuthenticated = false, whether there is a token in local storage or not. You should add additional logic to check the token in local storage before finally setting isAuthenticated. Probably the best place to put this code would be componentDidMount() function. I mean set it initially to false and then update in componentDidMount() according to current authorization status. Have a look at my GitHub, I have a small boilerplate project with such auth flow set-up. Hope this helps, happy coding!

I would use local storage instead of session storage like this
localStorage.setItem("jwt", jwtToken)
instead of the line
sessionStorage.setItem("jwt", jwtToken);
The check the local storage in the dev console, refresh the page and see if it is still there. It may require some other changes in your auth flow to build it off localStorage instead of sessionStorage; however, this will solve the immediate problem of losing the jwt on page refresh.

Related

React-gatsby login authentioncation failed

I am using Gatsby for my app. I have created one api from Mock api. My api looks like this. I have made one post-request for login, when user will put his/her email and password, if it does not match then it will alert "failed login" or if it is success it will alert ("successfully login") and navigate to successful page. But what ever email and password I am putting it always shows me I login successfully which is wrong logic. The email should be like my api's email: alak#gmail.com" and password: test123 . I think my logic right but still I am making the mistake. I share my code in Codesandbox.
PS: Codesandbox is based on react. but logic is same as my below code
Here is my code:
import React, { ReactElement, useState } from 'react';
import { PageProps, navigate } from 'gatsby';
import styled from 'styled-components';
import MainTemplate from '../templates/index';
import { TextInput } from '../components/textInput';
import { Button } from '../components/buttons';
import { API_URLS } from '../utilities';
interface Props extends PageProps {
}
export default function SignIn({ }: Props): ReactElement {
const [state, setState] = useState({
"email": ``,
"password": ``,
"loading": false
});
const { loading } = state;
const signInValue = (e) => {
setState({
...state,
[e.target.id]: e.target.value
});
};
const onSubmit = async (e) => {
e.preventDefault();
console.log(state);
setState({
"loading": true,
...state
});
const response = await fetch(`https://run.mocky.io/v3/beec46b8-8536-4cb1-9304-48e96d341461`, {
"method": `POST`,
"headers": {
"Accept": `application/json`,
'Content-Type': `application/json`
},
"body": { state }
});
if (response.ok) {
alert(`you have suceefully login`);
navigate(`/success`, { "state": { email } });
} else {
alert(`login failed`);
}
};
return (
<MainTemplate>
<TextInput
type="text"
value={state.email}
onChange={signInValue}
id="email"
required
/>
<TextInput
type="password"
value={state.password}
onChange={signInValue}
id="password"
required
/>
<Button
type="submit"
name="action"
onClick={onSubmit}
disabled={loading}
> {loading ? `loading...` : `save`}
</Button>
</MainTemplate>
);
}
According to the fetch specification:
The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.
So your api might be returning an error but response.ok might still be true. You should check response.status !== 200 instead.
Update: works as shown below based on your sandbox:

Firebase, React: How do I display http errors on client side?

I'm building a project with React and Firebase. I've split it into two repos - a backend repo for firebase functions, and a client-side repo for React. Part of my project involves user registration, and I want to be able to send any errors (eg, email already in use, weak password) to the client side, but I'm not sure how. Does anyone have suggestions about how I can pass backend errors to the client?
I know that I could move the firebase API call to client side which would make error handling much easier, but this would create complications.
Code as follows:
Registration component VenueRegister
import React from 'react'
import Header from './Header'
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button'
import axios from 'axios'
import { auth } from 'firebase/app'
class VenueRegister extends React.Component {
constructor() {
super();
this.state = {
email: '',
password:'',
confirmPassword:'',
venueName:'',
venueAddress:''
};
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e){
this.setState({
[e.target.name]: e.target.value
})
}
handleSubmit(e) {
console.log("submit function reached");
e.preventDefault();
const venueData = {
email: this.state.email,
password: this.state.password,
confirmPassword: this.state.confirmPassword,
venueName: this.state.venueName,
venueAddress: this.state.venueAddress
};
axios("http://localhost:5000/gig-fort/us-central1/api/registerVenue", {
method: "POST",
headers: {
"content-type": "application/json",
},
data: venueData,
})
.then((res) => {
console.log(res.data);
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<>
<Header />
<h1 className="heading-venue-reg">Register your venue</h1>
<form onSubmit = {this.handleSubmit}>
<TextField
required
id="standard-required"
name = 'email'
label="Required"
defaultValue="email"
placeholder="email"
onChange = {this.handleChange}
/>
<TextField
required
id="standard-required"
name = 'password'
label="Required"
defaultValue="password"
placeholder="password"
onChange = {this.handleChange}
/>
<TextField
required
id="standard-required"
name = 'confirmPassword'
label="Required"
defaultValue="confirm password"
placeholder="confirm password"
onChange = {this.handleChange}
/>
<TextField
required
id="standard-required"
name = 'venueName'
label="Required"
defaultValue="venue name"
placeholder="venue name"
onChange = {this.handleChange}
/>
<TextField
required
id="standard-required"
name = 'venueAddress'
label="Required"
defaultValue="venue address"
placeholder="venue address"
onChange = {this.handleChange}
/>
<Button type="submit">Submit</Button>
</form>
</>
);
}
}
export default VenueRegister
Backend function for venue registration:
app.post('/registerVenue',(req,res) =>{
const newVenue = {
email: req.body.email,
password: req.body.password,
confirmPassword: req.body.confirmPassword,
venueName: req.body.venueName,
venueAddress: req.body.venueAddress
}
let errors = {}
if(isEmpty(newVenue.email)){
errors.email = 'Email must not be empty'
}else if (!isEmail(newVenue.email)){
errors.email = 'must be a valid email address'
}
if(Object.keys(errors).length >0){
return res.status(400).json(errors)
}
let token, userId
db.doc(`/user/${newVenue.venueName}`).get()
.then(doc => {
if(doc.exists){
return res.status(400).json({venueName: `This venue name is already taken`})
} else {
return firebase
.auth()
.createUserWithEmailAndPassword(newVenue.email, newVenue.password)
}
})
.then(data=> {
userId = data.user.uid;
return data.user.getIdToken()
})
.then(idToken => {
token = idToken
const venueDetails ={
email: newVenue.email,
venueName: newVenue.venueName,
venueAddress: newVenue.venueAddress,
createdAt: new Date().toISOString(),
userId
}
return db.doc(`/users/${newVenue.venueName}`).set(venueDetails)
})
.then(() => {
return res.status(201).json({token})
})
.catch(err => {
console.error(err)
if(err.code === 'auth/email-already-in-use'){
return res.status(400).json({email: 'Email is already in use'})
} else {
return res.status(500).json({error:err.code})
}
//TODO: put in conditional statement that sends 'weak password error' to client
})
})
In the frontend, when receiving the data from the backend, you should check your response data for errors. It is very likely you would want a util function for that since it is a common task across almost all requests (hence re-utilize).
So you would like to check the response.status and then set a new state like a hasError or something that will cause a re-render to your component and show, i.e, a child component that displays the error to the user and gives the ability to retry the operation.

Losing data properties from firebase realtime database user collection

I know this question has been asked in other contexts. I've read those answers. I've tried the answers. I've read through the firebase docs. So, far it's not helping. I'm new to firebase. Learning how to use the product by going through a tutorial on using React and Firebase together. Building an authentication/authorization module for users. Can successfully register/SignUp a user and see the results in both the authorization module and the realtime database in the firebase project overview. When the user Signs Out, a check of the RDb confirms that all the data properties persist. When the user Signs In (email/password) again, a check of the RDb shows that only the uid and the email address persist in the RDb. The non-auth properties of the user like username and role become empty strings or objects, present but not populated. I have done considerable reading of the firebase docs and debugging of the code, but I cannot determine why this happens and therefore cannot determine how to fix it.
Here are some code segments:
First the firebase module:
import app from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'
... set up the config ...
// set the config constant to the appropriate value
const config = process.env.NODE_ENV === 'production' ? prodConfig : devConfig
class Firebase {
constructor() {
// initialize the app using the Firebase config to instatiate all of the firebase services
app.initializeApp(config)
// instantiate the initial auth state within the app
this.auth = app.auth() // Gets the Auth service for the current app
// instantiate the database within the app
this.db = app.database() // Gets the Database service for the current app
console.log('this.db ', this.db)
// Social media login providers (authorization methods within Firebase)
this.googleProvider = new app.auth.GoogleAuthProvider()
this.facebookProvider = new app.auth.FacebookAuthProvider()
this.twitterProvider = new app.auth.TwitterAuthProvider()
}
... the comments are mostly for my benefit ...
// ***** Firebase Auth API *****
// create user with email and password (equate doCreate... with Firebase createUser...)
doCreateUserWithEmailAndPassword = ( email, password ) => this.auth.createUserWithEmailAndPassword( email, password )
// ***** SignIn with email ***** (equate doSignIn... with Firebase signIn...)
doSignInWithEmailAndPassword = ( email, password ) => this.auth.signInWithEmailAndPassword( email, password )
// ***** SignIn with facebook ***** (equate as above)
doSignInWithFacebook = () => this.auth.signInWithPopup( this.facebookProvider )
// ***** SignIn with google ***** (equate as above)
doSignInWithGoogle = () => this.auth.signInWithPopup( this.googleProvider )
// ***** SignIn with twitter ***** (equate as above)
doSignInWithTwitter = () => this.auth.signInWithPopup( this.twitterProvider )
// ***** SignOut ***** (equate as above)
doSignOut = () => this.auth.signOut()
// ***** Password Reset ***** (equate as above)
doPasswordReset = email => this.auth.sendPasswordResetEmail( email )
// ***** Password Update ***** (equate as above)
doPasswordUpdate = password => this.auth.currentUser.updatePassword( password )
// ***** User API *****
// set the current user id
user = uid => this.db.ref(`users/${uid}`)
// set the reference to the users collection in the firebase database
users = () =>this.db.ref('users')
// ***** Merge Auth and DB User API *****
... big block of comments to me ...
onAuthUserListener = (next, fallback) =>
this.auth.onAuthStateChanged(authUser => {
if ( authUser ) {
this.user(authUser.uid)
.once('value')
.then(snapshot => {
const dbUser = snapshot.val()
console.log('this.user ',this.user)
console.log('dbUser ',dbUser)
// default empty roles
if ( !dbUser.roles ) {
dbUser.roles = {}
}
// merge auth and db user
this.db.ref(`users/${this.user}`).set({
uid: authUser.uid,
email: authUser.email,
username: authUser.username,
roles: authUser.roles,
...dbUser,
})
console.log('firebase.js authUser ', authUser)
next(authUser)
})
} else { // there is no authUser
fallback()
}
})
}
export default Firebase
Here is the Sign UP component:
// index.js - SignUp
// the entry point for the SignUp component
import React, { Component } from 'react'
import { Link, withRouter } from 'react-router-dom'
import { compose } from 'recompose'
import { Typography, Input, Checkbox, FormLabel, Button } from '#material-ui/core'
import { withFirebase } from '../Firebase'
import * as ROUTES from '../../constants/routes'
import * as ROLES from '../../constants/roles'
import '../../styles/auth.css'
const SignUpPage = () => (
<div id='wrapper' className='signup-page'>
<SignUpForm />
</div>
)
// initialize the state of the component using destructuring
// allows INITIAL_STATE to be reset after successful SignUp
const INITIAL_STATE = {
username: '',
email: '',
passwordOne: '',
passwordTwo: '',
isAdmin: false,
error: null,
}
class SignUpFormBase extends Component {
constructor(props) {
super(props)
// spread operator (...) spreads out to reach all properties individually
this.state = { ...INITIAL_STATE }
}
onSubmit = event => {
// get necessary info from this.state to pass to the Firebase authentication API
const { username, email, passwordOne, isAdmin } = this.state
const roles = {}
if ( isAdmin ) { roles[ROLES.ADMIN] = ROLES.ADMIN } // set roles if the admin checkbox is checked
this.props.firebase
// create a user (limited access) in the authentication database
.doCreateUserWithEmailAndPassword( email, passwordOne )
// successful
.then( authUser => {
// create a user in Firebase realtime database -- this is where you manage user properties
// because in the firebase auth module, users cannot be manipulated.
console.log('signup authUser ',authUser)
return this.props.firebase
.user(authUser.user.uid) // use the authUser.uid to:
.set({ username, email, roles }) // write username, email & roles to the rdb
})
.then(() => {
// update state and redirect to Home page
this.setState({ ...INITIAL_STATE })
this.props.history.push(ROUTES.HOME)
})
// error - setState, error (if something is wrong)
.catch(error => {
this.setState({ error })
})
// prevent default behavior (a reload of the browser)
event.preventDefault()
}
onChange = event => {
// dynamically set state properties when they change, based on which input call is executed
// each <input> element (in the return) operates on a different property of state (according to value)
this.setState({ [event.target.name]: event.target.value })
}
onChangeCheckbox = event => {
this.setState({ [event.target.name]: event.target.checked })
}
render() {
// parse each of the values from current state
const {
username,
email,
passwordOne,
passwordTwo,
isAdmin,
error
} = this.state
// list of invalid conditions for which to check (validation of form elements)
const isInvalid =
passwordOne !== passwordTwo ||
passwordOne === '' ||
email === '' ||
username === ''
return (
// the input form -- with fields (username, email, passwordOne, passwordTwo)
<div className='container'>
<Typography
variant='h6'
align = 'center'
className='item'
>
Sign Up Page
</Typography>
<br />
<form className='signup-form item' onSubmit={ this.onSubmit }>
<Input
className='item'
name='username'
value={username}
onChange={this.onChange}
type='text'
placeholder='Full Name'
/>
... input email, password, confirm password, checkbox to designate Admin ...
<Button
variant='contained'
className='item btn btn-secondary'
disabled={ isInvalid }
type='submit'
>
Sign Up
</Button>
{/* if there is an error (a default Firebase property), render the error message */}
{error && <p>{ error.message }</p>}
</form>
</div>
)
}
}
const SignUpLink = () => (
<Typography
variant = 'body1'
align = 'center'
className = 'item'
>
Don't have an account? <Link to={ ROUTES.SIGN_UP }>Sign Up</Link>
</Typography>
)
const SignUpForm = compose(withRouter, withFirebase)(SignUpFormBase)
export default SignUpPage
export { SignUpForm, SignUpLink }
Here is the Sign IN (email/password) component:
// SignInEmail.js - SignIn
// the email/password SignIn component
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'
import { compose } from 'recompose'
import { Typography, Input } from '#material-ui/core'
import Button from 'react-bootstrap/Button'
import { withFirebase } from '../Firebase'
import * as ROUTES from '../../constants/routes'
// initialize the state of the component using destructuring
// allows INITIAL_STATE to be reset after successful SignUp
const INITIAL_STATE = {
username: '',
email: '',
password: '',
roles: {},
error: null,
}
// ======================================
// ***** signin with email/password *****
// ======================================
class SignInEmailBase extends Component {
constructor(props) {
super(props)
// spread operator (...) spreads out to reach all properties individually
this.state = {
...INITIAL_STATE
}
}
onSubmit = event => {
// get necessary info from this.state to pass to the Firebase authentication API
// const { username, email, password, roles } = this.state
const { username, email, password, roles } = this.state
this.props.firebase
// execute SignIn function (create a user)
.doSignInWithEmailAndPassword( email, password )
// successful
.then(authUser => {
// create a user in Firebase Realtime database
console.log('signin authUser ',authUser)
return this.props.firebase
.user(authUser.user.uid)
.set({ username, email, roles })
})
.then(() => {
// update state and redirect to Home page
this.setState({ ...INITIAL_STATE })
this.props.history.push(ROUTES.HOME)
})
// error - setState, error (if something is wrong)
.catch(error => {
this.setState({
error
})
})
// prevent default behavior (a reload of the browser)
event.preventDefault()
}
onChange = event => {
// dynamically set state properties when they change, based on which input call is executed
// each <input> element (in the return) operates on a different property of state (according to value)
this.setState({ [event.target.name]: event.target.value })
}
render() {
// parse each of the values from current state
const { email, password, error } = this.state
// list of invalid conditions for which to check (validation of form elements)
const isInvalid = password === '' || email === ''
return (
// the input form -- with fields (username, email, passwordOne, passwordTwo)
<div className = 'container signin-page'>
<Typography
variant = 'h6'
align = 'center'
className = 'item'
>
Sign In Page
</Typography>
<br />
<form className = 'item email-form' onSubmit = { this.onSubmit} >
... input email, password...
{ /* disable the button if the form is invalid -- see isInvalid above */ }
<Button
className = 'item btn btn-secondary'
type = 'submit'
disabled = { isInvalid }
>
SIGN IN WITH EMAIL
</Button>
{ /* if there is an error (a default Firebase property), render the error message */ }
{ error && <p> { error.message } </p> }
</form>
</div>
)
}
}
const SignInEmail = compose(withRouter, withFirebase, )(SignInEmailBase)
export default SignInEmail
Here is the screenshot of the browser inspect and the screenshot of the Realtime Database entry after Sign UP
Here is the same user after sign out and sign in -- no other data manipulation
I sure could use some help. Thanks.
I sort of stumbled on to the answer to this myself. As I was reviewing my code and comparing the SignUp component with the SignIn component I realized that looked out of place within the SignIn component. It was this block within the onSubmit block:
this.props.firebase
// execute SignIn function (create a user)
.doSignInWithEmailAndPassword( email, password )
// successful
.then(authUser => {
// create a user in Firebase Realtime database
return this.props.firebase
.user(authUser.user.uid)
.set({ username, email, roles })
})
.then(() => {
// update state and redirect to Home page
this.setState({ ...INITIAL_STATE })
this.props.history.push(ROUTES.HOME)
})
// error - setState, error (if something is wrong)
.catch(error => {
this.setState({
error
})
})
It is identical to a similar block in the signUp component. It is necessary with the sign Up because I am creating a user with a signUp and the information that was disappearing in the signIn component is being entered in the signUp component so it is temporarily available in the authUser for transfer to the RdB. That information is not being entered in the signIn component so it is not available to transfer to the Realtime Database and hence (because of the way firebase works in syncing users between the auth module and the RdB) those properties were being over written with empty strings or empty objects. Further because the function is a signIn function there is no need at signIn time to update the RdB so the solution is to eliminate this block of code in the in the signIn component.
.then(authUser => {
// create a user in Firebase Realtime database
return this.props.firebase
.user(authUser.user.uid)
.set({ username, email, roles })
})
Sorry if I was a bother. But I did want to post that I figured it out.

How to solve Uncaught (in promise) TypeError in react authentication?

I am pretty new to MERN stack. I am trying to setup user authentication using jwt.
The problem is only occurring when I am using the front end to login. When I using POST man to make an http request, login successful is succesfull
However when I login using email and password I get the following error:
console
Uncaught (in promise) TypeError: Cannot read property 'data' of undefined
at authActions.js:40
I know it is showing where the error is but I am still unable to fix it.
authActions.js
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING } from "./types";
// Register User
export const registerUser = (userData, history) => dispatch => {
axios
.post("/api/users/register", userData)
.then(res => history.push("/login")) // re-direct to login on successful register
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Login - get user token
export const loginUser = userData => dispatch => {
axios
.post("/api/users/login", userData)
.then(res => {
// Save to localStorage
// Set token to localStorage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
// Set logged in user
export const setCurrentUser = decoded => {
return {
type: SET_CURRENT_USER,
payload: decoded
};
};
// User loading
export const setUserLoading = () => {
return {
type: USER_LOADING
};
};
// Log user out
export const logoutUser = () => dispatch => {
// Remove token from local storage
localStorage.removeItem("jwtToken");
// Remove auth header for future requests
setAuthToken(false);
// Set current user to empty object {} which will set isAuthenticated to false
dispatch(setCurrentUser({}));
};
Following is my code for login front end.
login.jsx
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: ""
};
}
componentDidMount() {
// If logged in and user navigates to Login page, should redirect them to dashboard
if (this.props.auth.isAuthenticated) {
this.props.history.push("/dashboard");
}
}
validateForm() {
return this.state.email.length > 0 && this.state.password.length > 0;
}
componentWillReceiveProps(nextProps) {
if (nextProps.auth.isAuthenticated) {
this.props.history.push("/dashboard"); // push user to dashboard when they login
}
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
};
handleSubmit = event => {
event.preventDefault();
console.log("Submit called");
const userData = {
email: this.state.email,
password: this.state.password
};
this.props.loginUser(userData);
};
render() {
const errors = this.state;
return (
<div className="Login">
<form onSubmit={this.handleSubmit}>
<FormGroup controlId="email" bs="large">
<FormLabel>Email</FormLabel>
<span className="red-text">{errors.emailnotfound}</span>
<FormControl
autoFocus
type="email"
value={this.state.email}
onChange={this.handleChange}
className={classnames("", {
invalid: errors.email || errors.emailnotfound
})}
/>
</FormGroup>
<FormGroup controlId="password" bs="large">
<FormLabel>Password</FormLabel>
<span className="red-text">
{errors.password}
{errors.passwordincorrect}
</span>
<FormControl
value={this.state.password}
onChange={this.handleChange}
type="password"
className={classnames("", {
invalid: errors.password || errors.passwordincorrect
})}
/>
</FormGroup>
<Button
block
bs="large"
disabled={!this.validateForm()}
type="submit"
>
Login
</Button>
<br />
<p> Dont have account ? </p>
<Link to="/register">
{" "}
<p style={{ color: "blue" }}> Join Us </p>{" "}
</Link>
</form>
<br />
</div>
);
}
}
I have left out some import statements to shorten the code.
The error is on line 40, wich is in payload: err.response.data.
If it says
Cannot read property 'data' of undefined
It means that err.response is ndefined.
You should do some checking before passing that. Maybe it's returning a different error that doesn't have .response.
Try console.log(err) to check what is inside of it.
It looks that you don't stringify the data before you send it. Require the qs library and change your code to that:
axios
.post("/api/users/register", qs.stringify( userData ))
.then(res => history.push("/login")) // re-direct to login on successful register
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
You can also check that the data is not sent correctly by looking the Network tab in your Browser's Dev Tools. I expect to send the data in a wrong format. I hope that I am correct.
You need to update your code from
onSubmit={this.handleSubmit} to onSubmit={()=>this.handleSubmit()}
And
onChange={this.handleChange}to onChange={()=>this.handleChange()}
because of this your password and email are not getting set to state and your API axios.post("/api/users/login", userData) is throwing exception.

Axios.post returning :ERR_EMPTY_RESPONSE after API call

I've made a axios post request in my react component, which makes a request to twilio to send a text message to my phone from a route on my server.
The text message and payload are transmitted successfully however when opening the network tab in the console I get this error in a minute or two.
POST http://localhost:8080/api/twilio net::ERR_EMPTY_RESPONSE
Any notion out there how to solve this?
This is the code from my react component:
import React, { Component } from 'react';
import axios from 'axios';
import { Grid, Segment, Form } from 'semantic-ui-react';
import './test.css';
class Test extends Component {
constructor(props) {
super(props);
this.state = { phonenumber: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ phonenumber: event.target.value });
}
handleSubmit() {
return axios
.post('/api/twilio', {
phonenumber: this.state.phonenumber,
})
.then(resp => resp.data)
.catch(error => console.log(error));
}
render() {
const { phonenumber } = this.state;
console.log('phonenumber', phonenumber);
return (
<Grid columns={1} stackable textAlign="center">
<Grid.Column width={1} />
<Grid.Column width={14}>
<Form onSubmit={this.handleSubmit}>
<Segment stacked>
<Form.Group id="form-group" inline>
<label>Phone Number</label>
<Form.Input onChange={this.handleChange} value={phonenumber} placeholder="+12223334444" />
</Form.Group>
<Form.Button id="form-group-button" content="Submit" />
</Segment>
</Form>
</Grid.Column>
<Grid.Column width={1} />
</Grid>
);
}
}
export default Test;
Update:
This is the twilio route on the backend.
const router = require('express').Router();
module.exports = router;
router.post('/', (req, res) => {
let SID = 'ACc5b16ad0cefc3b514e69bc30636726e2';
let TOKEN = '3145fb41afe308f22b0b7c647e6a8e17';
let SENDER = '+18622256079';
if (!SID || !TOKEN) {
return res.json({ message: 'add TWILIO_SID and TWILIO_TOKEN to .env file.' });
}
let client = require('twilio')(SID, TOKEN);
client.messages
.create({
to: req.body.phonenumber,
from: SENDER,
body: 'This is the ship that made the Kessel Run in fourteen parsecs?',
})
.then(message => console.log(message.sid));
});
In your route on the server, nothing will ever be returned to the client because SID and TOKEN are always defined (at least in your example). To make sure that the request will not fail, you will need to send at least some response back after the Twilio request, e.g.:
client.messages
.create({
to: req.body.phonenumber,
from: SENDER,
body: 'This is the ship that made the Kessel Run in fourteen parsecs?'
})
.then(message => {
console.log(message.sid);
// Either just send an empty, successful response or some data (e.g. the `sid`)
res.status(200).send(message.sid);
})
.catch(err => {
console.log(err);
// In case of an error, let the client know as well.
res.status(500).send(err);
});

Resources