I have a handleLogin function in an auth.js file, I would like to retrieve the errors received in the .catch and transmit them in my Alert.js file in order to display the errors in an alert in Login.js...
This is my handleLogin function on auth.js
export const handleLogin = async ({ email, password }) => {
const user = await ooth.authenticate('local', 'login', {
username: email,
password: password,
}).catch(e => {
alert(e.message)
});
await navigate(`/app/profile`);
if (user) {
return setUser({
id: user._id,
username: `jovaan`,
name: `Jovan`,
email: user.local.email,
avatar: `3`,
telephone: `0788962157`,
bio: `I'm a front-end dev`
})
}
return false
}
My Alert.js
import React from "react";
import { Alert } from "shards-react";
export default class DismissibleAlert extends React.Component {
constructor(props) {
super(props);
this.dismiss = this.dismiss.bind(this);
this.state = { visible: true, message: "Message par défaut" };
}
render() {
return (
<Alert dismissible={this.dismiss} open={this.state.visible} theme="success">
{this.message()}
</Alert>
);
}
dismiss() {
this.setState({ visible: false });
}
message() {
return this.state.message
}
}
I imported my Alert.js into my Login page, so I currently have the default message
You will need to somehow pass the results of the failed login via props wherever you have the <DismissableAlert /> in your code, which I'm presuming would be at /login or similar. My guess would be to do something like this, by passing the error via the 'state' option that navigate provides:
const error = null;
const user = await ooth.authenticate('local', 'login', {
username: email,
password: password,
}).catch(e => {
error = e
console.err(e.message)
});
if(error) {
await navigate(`/login`, {
state: { error: error },
});
}
Then in your LoginPage component (or whatever it is called), you should then have access to the result of the error as props that you can then pass down to the Alert:
render() {
const {error} = this.props
return(
<div>
<LoginForm />
{error &&
<DismissibleAlert message={error.message} />
}
</div>
)
}
Or something along those lines, depending on your components. I notice you have a default message in the DismissableAlert, so this is just an example to show that you could pass the error in to use it to show a relevant message.
Related
Warning: Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in a useEffect
cleanup function.
I have tried everything to fix but nothing works. I didn't even use "useEffect". This happens when I login to account and navigate the user to search page.
loginScreen.js
const validationSchema = Yup.object().shape({
email: Yup.string()
.label("Email")
.email("Enter a valid email")
.required("Please enter a registered email"),
password: Yup.string()
.label("Password")
.required()
.min(6, "Password must have at least 6 characters "),
});
const { width, height } = Dimensions.get("window");
class loginScreen extends React.Component {
state = {
passwordVisibility: true,
rightIcon: "ios-eye",
};
goToSignup = () => this.props.navigation.navigate("Signup");
handlePasswordVisibility = () => {
this.setState((prevState) => ({
rightIcon: prevState.rightIcon === "ios-eye" ? "ios-eye-off" : "ios-eye",
passwordVisibility: !prevState.passwordVisibility,
}));
};
handleOnLogin = async (values, actions) => {
const { email, password } = values;
try {
const response = await this.props.firebase.loginWithEmail(
email,
password
);
if (response.user) {
this.props.navigation.navigate("App");
}
} catch (error) {
alert("Seems like there is no account like that. Try something else.");
} finally {
actions.setSubmitting(false);
}
};
searchScreen.js
class searchScreen extends Component {
apiurl = "";
_isMounted = false;
constructor(props) {
super(props);
this.state = {
searchText: "",
results: [],
isLoading: true,
};
}
showMovie = async (imdbID) => {
await axios(this.apiurl + "&i=" + imdbID).then(({ data }) => {
let result = data;
this.props.navigation.navigate("Movie", {
selected: result,
movieID: imdbID,
});
});
};
componentDidMount() {
this._isMounted = true;
this.initial();
}
componentWillUnmount() {
this._isMounted = false;
}
initial = async () => {
const user = await this.props.firebase.getUser(user);
try {
await AsyncStorage.setItem("useruid", user.uid);
} catch (error) {
console.log(error);
}
const expoToken = await UserPermissions.registerForPushNotificationsAsync();
if (expoToken) {
this.props.firebase.setExpoToken(expoToken);
}
if (this._isMounted) {
this.setState({ isLoading: false });
}
};
search = async () => {
Keyboard.dismiss();
await axios(this.apiurl + "&s=" + this.state.searchText).then(
({ data }) => {
let results = data.Search;
if (this._isMounted) {
this.setState((prevState) => {
return { ...prevState, results: results };
});
}
}
);
};
After a successful login, you call the navigate function. This navigates to a different component, which means the login component becomes unmounted. The handleLogin function still has more logic to execute though, in your finally statement, you are setting submission to false. When that finally runs there is no mounted component which means there is no state to set.
Moving your submission false state change to before the navigate call and before the alert will solve the problem.
I’d recommend not bothering with it in the case the user actually logs in, because the user is about to visually move to a completely different screen, changing the state doesn’t really help them.
Having some trouble sending data from my frontend to my server. I put in multiple console.log()'s to make reading the error messages and following the data easier.
Basically, the proper information is put into the axios.get call, and sent to the server. However, the server is receiving the information as 'undefined'. This can be seen in the console outputs/errors.
I am wondering if this is due to my frontend being normal javascript, and my server being in typescript?
Here is my full login component (React/Redux):
import React, { Component } from 'react';
import FormField from '../utils/Form/formfield';
import { update, generateData, isFormValid } from '../utils/Form/formActions';
import { withRouter } from 'react-router-dom';
import { loginUser } from '../../actions/user_actions';
import { connect} from 'react-redux';
class Login extends Component {
state={
formError: false,
formSuccuss: '',
formdata:{
email: {
element: 'input',
value: '',
config: {
name: 'email_input',
type: 'email',
placeholders: 'Enter your email'
},
validation:{
required: true,
email: true
},
valid: false,
touched: false,
valdationMessage:''
},
password: {
element: 'input',
value: '',
config: {
name: 'password_input',
type: 'password',
placeholders: 'Enter your password'
},
validation:{
required: true,
},
valid: false,
touched: false,
valdationMessage:''
}
}
}
updateForm = (element) => {
const newFormdata = update(element,this.state.formdata,'login');
this.setState({
formError: false,
formdata: newFormdata
})
}
submitForm= (event) =>{
event.preventDefault();
console.log('submitting form!!!!!');
let dataToSubmit = generateData(this.state.formdata,'login');
let formIsValid = isFormValid(this.state.formdata,'login');
console.log(dataToSubmit);
if(formIsValid ){
console.log("form is valid guy!")
console.log(dataToSubmit);
this.props.dispatch(loginUser(dataToSubmit)).then(response =>{
if(response.payload.loginSuccess){
console.log(response.payload);
this.props.history.push('/user/dashboard');
}else{
this.setState({
formError: true
})
}
});
} else {
this.setState({
formError: true
})
}
}
render() {
return (
<div className="signin_wrapper">
<form onSubmit={(event)=> this.submitForm(event)}>
<FormField
id={'email'}
formdata={this.state.formdata.email}
change={(element)=> this.updateForm(element)}
/>
<FormField
id={'password'}
formdata={this.state.formdata.password}
change={(element)=> this.updateForm(element)}
/>
{ this.state.formError ?
<div className="error_label">
Please check your data
</div>
:null}
<button onClick={(event)=> this.submitForm(event)}>
Log in
</button>
<button
style={{marginLeft:'10px'}}
onClick={()=> this.props.history.push('/reset_user') }>
Forgot my password
</button>
</form>
</div>
);
}
}
export default connect()(withRouter(Login));
Here is the Redux action using redux-promise:
export function loginUser(dataToSubmit){
console.log("Inside the action");
console.log(dataToSubmit);
console.log("outside the action");
const request = axios.get(`http://localhost:3333/users/login`,dataToSubmit)
.then(response => response.data);
return {
type: LOGIN_USER,
payload: request
}
}
This is the login's output. All data seems fine:
login.jsx:64 submitting form!!!!!
login.jsx:68 {email: "anemail#gmail.com", password: "passwords"
login.jsx:71 form is valid guy!
login.jsx:72 {email: "anemail#gmail.com", password: "passwords1"}
user_actions.js:87 Inside the action
user_actions.js:88 {email: "anemail#gmail.com", password: "passwords1"}
user_actions.js:89 outside the action
Server's index.ts using express and Typescript:
import express from 'express';
import bodyParser from 'body-parser'; import { userRouter } from './routers/user-router';
const app=express();
const port=3333; app.set('port', port);
app.use(bodyParser.json({limit: '16mb'}))
app.use((req, resp, next) => {
resp.header("Access-Control-Allow-Origin", http://localhost:3000); resp.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
resp.header("Access-Control-Allow-Credentials", "true");
resp.header("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE"); next(); });
app.use('/users', userRouter);
const server = app.listen(port, () => {
console.log(App is running at http://localhost:${port}); });
This is the router that receives the data, which is also in typescript:
userRouter.get("/login", async (req:Request, resp: Response) => {
try {
console.log("Attempting to login user");
console.log(req.body.email);
console.log(req.body.password);
const user = await userDao.getUser(
req.body.email,
req.body.password
);
console.log("Got User");
console.log(user);
resp.json(user);
} catch (err) {
console.log(err);
resp.sendStatus(500);
}
});
As you can see here, the data the server receives from the frontend is instantly rendered as 'undefined' even though the frontend displayed the proper information:
Attempting to login user
undefined
undefined
TypeError: Cannot read property 'account_id' of undefined
at Object.userConverter (C:\GitFolder\project2\server\util\converters\userConverter.ts:6:11)
at Object.<anonymous> (C:\GitFolder\project2\server\dao\user-dao.ts:75:29)
at Generator.next (<anonymous>)
at fulfilled (C:\GitFolder\project2\server\dao\user-dao.ts:5:58)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
Got User
undefined
Any help would be greatly appreciated, and I can add more code if needed
You cannot attach a body to a GET request.
You might want to change your request within the Frontend to POST and read the request as POST on your Backend, then I am sure your body will be available for reading.
GET requests usually have parameters sent within the path itself such as:
http://your.backend.com/score/:userId/:levelId
POST requests on the other end can have a body attached to it.
You should return within the then function of promise.
export function loginUser(dataToSubmit){
console.log("Inside the action");
console.log(dataToSubmit);
console.log("outside the action");
axios.post(`http://localhost:3333/users/login`, dataToSubmit).then(response => {
return {
type: LOGIN_USER,
payload: response.data
}
}).catch(err => {
console.log(err)
})
}
Or you can use await like this
const myFunc=async (dataToSubmit)=>{
console.log("Inside the action");
console.log(dataToSubmit);
console.log("outside the action");
const response = await axios.post(`http://localhost:3333/users/login`,dataToSubmit)
return {
type: LOGIN_USER,
payload: response.data
}
}
post is used for login as we don't send the body in a get request. check the code below.
I have an Ooth authentication server with PassportJS, a GraphQL API and a Gatsby client, I want to get my error messages in a bootstrap alert when my handleLogin function returns the catch, how can I do that?
auth.js
export const handleLogin = async ({ email, password }) => {
const user = await ooth.authenticate('local', 'login', {
username: email,
password: password,
}).catch(e => {
console.log(e)
});
await navigate(`/app/profile`);
if (user) {
return setUser({
id: user._id,
username: `jovaan`,
name: `Jovan`,
email: user.local.email,
avatar: `3`,
telephone: `0788962157`,
bio: `I'm a front-end dev`
})
}
return false
}
Alert.js
import React from "react";
import { Alert } from "shards-react";
export default class DismissibleAlert extends React.Component {
constructor(props) {
super(props);
this.dismiss = this.dismiss.bind(this);
this.state = { visible: true, message: "Message par défaut" };
}
render() {
return (
<Alert dismissible={this.dismiss} open={this.state.visible} theme="success">
{this.message()}
</Alert>
);
}
dismiss() {
this.setState({ visible: false });
}
message() {
return this.state.message
}
}
In your api, you can send a return a response with a 401 status code along with a JSON string. In express:
.catch(e => {
res.status(401).json({ message: 'Login Failed!' /*or e if you want that*/ });
})
Now from your react application, you can set the message state in the catch block of your api request.
Why not send a prop to the Alert component?
In your auth.js error handler,
<DismissibleAlert visible={true} />
in your Alert.js add
componentWillReceiveProps(nextProps) {
// You don't have to do this check first, but it can help prevent an unnecessary render
if (nextProps.visible !== this.state.visible) {
this.setState({ visible: nextProps.visible });
}
}
There are a lot you can do to optimise your code. You can read the article below.
https://medium.com/#ruthmpardee/passing-data-between-react-components-103ad82ebd17
** Update
After looking at your code, Im just going to focus on showing the alert.
first, remove await navigate(/app/profile); from your auth.js.
then in your login.js do this
handleSubmit = event => {
event.preventDefault()
if(!handleLogin(this.state)){
this.setState({showErrorModal: true, modalMessage: "unable to login"});
}
/* rest of code, like navigating the user out */
}
DismissibleAlert in your render() would be <DismissibleAlert visible={this.state.showErrorModal} message={this.state.modalMessage} />.
the rest would be as stated above.
to help: your alert.js
export default class DismissibleAlert extends React.Component {
...
componentWillReceiveProps(nextProps) {
// You don't have to do this check first, but it can help prevent an unnecessary render
if (nextProps.visible !== this.state.visible) {
this.setState({ visible: nextProps.visible });
}
}
}
I suggest you read this too
React component initialize state from props
I am learning React-Redux and trying to get more comfortable with it by implementing things in various ways. I have a login form where I want to display an error message if the username/password is invalid. I have created config file with the required user details. I am calling an authenticate api to generate a JWT token for the logged in user.So, the token you get as response of the authenticate api will have the logged in user details. I have done something like below but I see I am able to successfully login every time and not able to display any error message when I try to provide any random/wrong user name. I have commented out the componetWillreceiveProps function now but would like to understand what I am doing wrong.
My Log in Comp-
import React from "react";
import Header from "./header";
import Footer from "./footer";
import { connect } from "react-redux";
import { createLogIn, setAuthError } from "../actions/action";
const axios = require("axios");
import jwtdata from "../config/jwtdata";
class Login extends React.Component {
constructor() {
super();
this.state = {
account: { user: "", password: "" }
};
}
handleAccountChange = ({ target: input }) => {
const account = { ...this.state.account };
account[input.name] = input.value;
this.setState({ account });
};
handleLoginForm = e => {
e.preventDefault();
let postLoginData = {};
const userName = this.state.account.user;
// call to action
this.props.dispatch(createLogIn(postLoginData, userName));
this.props.dispatch(setAuthError())
this.props.history.push("/intro");
};
// componentWillReceiveProps(nextProps) {
// if (nextProps.authStatus){
// this.props.history.push("/intro");
// }
// }
render() {
const { account } = this.state;
return (
<div className="intro">
<Header />
<form onSubmit={this.handleLoginForm}>
<div className="content container">
<div className="profile" />
<div className="row">
<div className="col-xs-12">
<input
type="text"
autoFocus
placeholder="username"
name="user"
value={account.user}
onChange={this.handleAccountChange}
/>
<input
type="password"
placeholder="password"
name="password"
value={account.password}
onChange={this.handleAccountChange}
/>
<button
className={
"loginButton " +
(account.user && account.password
? "not-disabled"
: "disabled")
}
disabled={!account.user && !account.password ? true : false}
>
<span>Sign in</span>
</button>
</div>
{!this.props.authStatus ? (
<p className="login-error">
Authorization Failed. Please try again!
</p>
) : (
<p />
)}
</div>
</div>
</form>
<Footer />
</div>
);
}
}
const mapStateToProps = state => ({
authStatus: state.root.authStatus
});
export default connect(mapStateToProps)(Login);
Action creator-
export const createLogIn = (postLoginData, userName) => (dispatch) => {
console.log('>>> ', userName);
console.log('authenticating');
console.log(btoa(JSON.stringify(jwtdata)));
localStorage.setItem("UserData", btoa(JSON.stringify(jwtdata[userName])))
// dispatch({
// type: SET_AUTH_ERROR,
// payload: false
// })
axios({
method: "POST",
url: "/authenticateUrl",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: postLoginData
})
.then (response => {
dispatch({
type: API_LOG_IN,
payload: response.data
})
localStorage.setItem('AccessToken', response.data.jwt_token);
})
.catch( error => {
console.log("in catch block");
});
}
export const setAuthError = () => {
console.log('inside actions');
return {
type: SET_AUTH_ERROR,
payload: "Authorization Error"
}
}
Reducer-
const initialState = {
authStatus: true
}
const reducerFunc = (state = initialState, action) => {
switch (action.type) {
case API_LOG_IN:
console.log('reducers');
return {...state, logIn: action.payload}
case SET_AUTH_ERROR:
console.log('inside Auth reduccer');
return {...state,authStatus: action.payload}
default: return {...state}
}
}
export default reducerFunc;
I am trying to add a check inside componentWillReceiveProps but that doesn't seem to be working.Instead it always displays me the error message even when the user name is same as the config file.What I want is to display some message like "Authorization failed" if I try to hit the sign in button with wrong User credentials.
!this.props.authStatus ? (
Seems like this line is the problem. Since your authStatus is either "undefined" when no error occurred or "Authentication failed".
I am using the following Container code to show a SnackBar if users email already exists with another account, If i remove this line of code (this.setState({ open: true })) The snack bar shows up fine by setting the state using a button :
class HomeContainer extends Component {
static propTypes = {
authError: PropTypes.string,
removeError: PropTypes.func.isRequired
}
static contextTypes = {
router: PropTypes.object.isRequired
}
constructor (props) {
super(props)
this.state = {
open: false
}
}
// ////////////////////////////////////////////////////////////////////////////////////////////////
// the following componetdid mount function is only required when using SigninwithRedirect Method - remove when using SigninWithPopup Method
componentDidMount () {
firebaseAuth.getRedirectResult().then((authUser) => {
// The signed-in user info.
console.log('User Data from Oauth Redirect: ', authUser)
const userData = authUser.user.providerData[0]
const userInfo = formatUserInfo(userData.displayName, userData.photoURL, userData.email, authUser.user.uid)
return (this.props.fetchingUserSuccess(authUser.user.uid, userInfo))
})
.then((user) => {
return saveUser((user.user))
})
.then((user) => {
return (this.props.authUser(user.uid))
}).then((user) => {
this.context.router.replace('feed')
}).catch(function (error) {
// Handle Errors here.
const errorCode = error.code
const errorMessage = error.message
// The email of the user's account used.
const email = error.email
// The firebase.auth.AuthCredential type that was used.
const credential = error.credential
// if error is that there is already an account with that email
if (error.code === 'auth/account-exists-with-different-credential') {
// console.log(errorMessage)
firebaseAuth.fetchProvidersForEmail(email).then(function (providers) {
// If the user has several providers,
// the first provider in the list will be the "recommended" provider to use.
console.log('A account already exists with this email, Use this to sign in: ', providers[0])
if (providers[0] === 'google.com') {
this.setState({ open: true })
}
})
}
})
}
handleRequestClose = () => {
this.setState({
open: false
})
}
// /////////////////////////////////////////////////////////////////////////////////////////////////////
render () {
return (
<div>
<Snackbar
open={this.state.open}
message= 'A account already exists with this email'
autoHideDuration={4000}
onRequestClose={this.handleRequestClose.bind(this)}/>
<Home />
</div>
)
}
}
I was using older function syntax at function (providers) & function (error) . switched them to arrow functions and the outside scope of this went down is defined. :)