I am building an app with ReactJS that authenticate users through a Laravel API.
This is a very simple app. And the authentication part is working well. But after authentication when I try to change the state I am getting this error.
Cannot update during an existing state transition
The App I am trying to build is a very simple one. After the app makes an API call to authenticate the user, it must get the user's data from the API response and pass those responses to the component set for logged in users.
I made some search on the error Cannot update during an existing state transition. All the answers I am getting are telling me not to setState inside of a render. I am not doing that.
Here is my code
App.jsx
//...
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false,
user: {}
};
}
_loginUser = (email, password) => {
var formData = new FormData();
formData.append("email", email);
formData.append("password", password);
axios
.post("http://localhost:8000/api/user/login", formData)
.then(response => {
console.log(response);
return response;
})
.then(json => {
if (json.data.success) {
let userData = {
first_name: json.data.data.first_name,
name: json.data.data.name,
id: json.data.data.email,
email: json.data.data.email,
auth_token: json.data.data.auth_token,
timestamp: new Date().toString()
};
let appState = {
isLoggedIn: true,
user: userData
};
//Save app state with user date in local storage
localStorage["appState"] = JSON.stringify(appState);
this.setState({
isLoggedIn: appState.isLoggedIn,
user: appState.user
});
} else {
$("#feedbacks").html(
`<span className="alert alert-danger">Login Failed: ${
json.data.data
}</span>`
);
}
$("#email-login-btn")
.removeAttr("disabled")
.html("Login");
})
.catch(error => {
$("#feedbacks").html(
`<span className="alert alert-danger">An Error Occured</span>`
);
$("#email-login-btn")
.removeAttr("disabled")
.html("Login");
});
};
componentDidMount() {
let state = localStorage["appState"];
if (state) {
let AppState = JSON.parse(state);
console.log(AppState);
this.setState({
isLoggedIn: AppState.isLoggedIn,
user: AppState
});
}
}
render() {
if (
!this.state.isLoggedIn &&
this.props.location.pathname !== "/app/login"
) {
this.props.history.push("/app/login");
}
if (
this.state.isLoggedIn &&
(this.props.location.pathname === "/app/login" ||
this.props.location.pathname === "/app/register")
) {
this.props.history.push("/");
}
return (
<Switch data="data">
<Route
exact
path="/"
render={props => (
<Home
{...props}
logOut={this._logoutUser}
isLoggedIn={this.state.isLoggedIn}
user={this.state.user}
/>
)}
/>
<Route
path="/app/login"
render={props => (
<Login {...props} loginUser={this._loginUser} />
)}
/>
</Switch>
);
}
}
const AppContainer = withRouter(props => <App {...props} />);
render(
<BrowserRouter>
<AppContainer />
</BrowserRouter>,
document.getElementById("root")
);
Login.jsx
const Login = ({ history, loginUser = f => f }) => {
console.log(history);
let _email, _password;
const handleLogin = e => {
e.preventDefault();
loginUser(_email.value, _password.value);
};
return (
<div>
<div id="wrapper" className="formWrapper">
<form method="post" action="#" onSubmit={handleLogin}>
<div className="fields">
<div className="field">
<label htmlFor="email">Your Email</label>
<input
ref={input => (_email = input)}
autoComplete="off"
id="email"
name="email"
type="text"
className="center-block"
placeholder="email"
/>
</div>
<div className="field">
<label htmlFor="password">Your Password</label>
<input
ref={input => (_password = input)}
autoComplete="off"
id="password-input"
name="password"
type="password"
className="center-block"
placeholder="password"
/>
</div>
</div>
<div className="actions login">
<button
type="submit"
className="primary"
id="email-login-btn"
>
<span className="icon fa-user" /> Login
</button>
</div>
</form>
</div>
<div id="blur" />
<div id="bg" />
</div>
);
};
export default Login;
The _login method handles the form submit. And within that method, I setState the user once authentication is valid. Apparently, that is where the error is coming from. That generates the error :
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
So I need to know where is the right place to change the value of the state 'user'.
Can someone help, please?
Related
I'm doing a login page with JWT and trying to redirect the page once log in to the home using this.props.history.push("/") but I get this typeError: Cannot read property 'push'of undefined. I don't know what I am doing wrong.
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "cihemzine#gmail.com",
password: "test",
};
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
login = () => {
const { email, password} = this.state;
axios("/users/login", {
method: "POST",
data: {
email,
password
},
})
.then((response) => {
localStorage.setItem('token', response.data.token);
console.log(response.data);
this.props.history.push("/");
})
.catch((error) => {
console.log(error);
});
};
render() {
return (
<div>
<div className="container">
<input value={this.state.email} onChange={this.handleChange} className="form-control mb-2 mt-4"name="email" type="text"placeholder="Your email" />
<input value={this.state.password} onChange={this.handleChange} className="form-control mb-2"name="password" type="password" placeholder="Your password" /><br></br>
<button onClick={this.login}className="text-center btn btn-light">LOGIN</button>
</div>
</div>
)
}
}
export default Login;
If you have nothing like 'history, match etc' in your props you need to check that you use your login component as Route
<Route path ='' component={Login} />
If you don't use Login in such way and can't for some reasons.
You can use HOC or hook for you component to add history to your component.
import { useHistory } from "react-router-dom"
or
import { withRouter } from "react-router";
I am using building a react app Here I am unable to figure out how to redirect to /dashboard after successfull login. Any help would be usefull
In this file i am passing my username and password to my redux action this.props.login
signin.js
handleLoginClick = () => {
const { inputUserID, inputPassword } = this.state;
this.login(inputUserID, inputPassword)
};
login = (username, password) => {
this.props.login(username, password);
};
render() {
const {
showError,
open,
inputUserID,
inputPassword,
checkRememberID,
showPhoneNumberDialog,
showVerifyNumberDialog
} = this.state;
return (
<div className="sign-in-dialog-container">
<Dialog
id="dialog-sign-in"
className="dialog"
open={open}
onClose={this.handleClose}
aria-labelledby="form-dialog-title"
style={{
backgroundColor: '#fff'
}}
>
<DialogTitle id="form-dialog-title">Sign In</DialogTitle>
<DialogContent className="dialog-content">
<div className="dialog-content-form">
<div className="form-field">
<div className="content-label">ID</div>
<FormControl fullWidth variant="outlined">
<OutlinedInput
fullWidth
type="text"
placeholder="User Name"
value={inputUserID}
onChange={this.handleTextChange("inputUserID")}
labelWidth={0}
/>
</FormControl>
</div>
<div className="form-field margin-top-16">
<div className="content-label">Password</div>
<FormControl fullWidth variant="outlined">
<OutlinedInput
fullWidth
type="password"
placeholder="**********"
value={inputPassword}
onChange={this.handleTextChange("inputPassword")}
labelWidth={0}
/>
{showError ? (
<FormHelperText className="password-incorrect-text">
Password is incorrect
</FormHelperText>
) : null}
</FormControl>
</div>
<div className="form-field">
<FormControlLabel
control={
<Checkbox
checked={checkRememberID}
onChange={this.handleCheckboxChange("checkRememberID")}
value="checkRememberID"
color="primary"
/>
}
label="Remember ID"
/>
</div>
<div className="form-field">
<Button
className="next-button custom-button-style"
fullWidth
onClick={this.handleLoginClick}
variant="contained"
color="primary"
>
Next
</Button>
</div>
</div>
</DialogContent>
<div className="bottom-row">
<span className="helper-text">
Don't have an account?
<span
onClick={this.handleSignUpClick}
className="strong cursor-pointer"
>
{" "}
Sign Up
</span>
</span>
</div>
</Dialog>
</div>
);
}
}
const mapStateToProps = state => ({
user: state.userReducer,
productPageReducer: state.productPageReducer,
isAuthenticated: state.auth.access_token,
});
const mapDispatchToProps = {
getProductList,
login,
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(SignIn);
authAction.js
export const login = (username, password) => (dispatch) => {
//User Loading:
dispatch({ type: USER_LOADING });
//Make API Call here
var myHeaders = new Headers();
myHeaders.append("Authorization", "Basic dG9wc2VsbGVyOnRvcHNlbGxlcg==");
var formdata = new FormData();
formdata.append("username", username);
formdata.append("password", password);
formdata.append("grant_type", "password");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
fetch("https://api.XXXXXXXXXXXXX.com/oauth/token", requestOptions)
.then(response => response.json())
.then(
res => {
if (res.access_token && res.refresh_token) {
window.localStorage.access_token = res.access_token;
window.localStorage.refresh_token = res.refresh_token;
dispatch({
type: LOGIN_SUCCESS,
user: res
});
} else {
dispatch({
type: LOGIN_FAIL,
message: 'Unauthorised User'
})
}
}
)
.catch(error =>
dispatch({
type: LOGIN_FAIL,
message: error
})
);
}
Here i want to figure out how to redirect to /dashboard after successfull login.I am also using react-router-dom.
You can do this but DO NOT:
CASE LOGIN_SUCCESS:
window.location.href = '/dashboard';
break;
This is just a hack. Redirecting with window.location.href makes your app lose SPA advantages.
Since you're using React-redux, you must hold user login state in reducer. And let App.jsx subscribe login state and if it's false render a Redirect component.
Why your app forgets login state after redirect:
Because you're doing something wrong with window.localStorage:
window.localStorage.whatever_token = 'foobarbaz';
This won't work. localStorage was not meant to be used like this.
Instead, you should:
window.localStorage.setItem('my_token', 'awesome_jwt_token');
Later:
const isLoggedIn = () = > {
const token = window.localStorage.getItem('my_token');
if (token) {
// can ommit this flow
// const isLoggedIn = askServerIfThisTokenIsValid(token);
// if (isLoggedIn) {
// return true;
// } else {
// window.localStorage.removeItem('my_token');
// }
return true; // but not safe - handle 403 error in all api calls
} else {
return false;
}
};
It seems like after successful sign-in attempt, { user } state at your redux store change.
therefore you might consider add this at signin.js
import {Redirect } from 'react-router-dom';
class SignIn extends Component {
...rest class..
render ()
const { user } = this.props;
return(
<>
{ if (user)<Redirect to="/dashboard" />;} // in case { user } is null at initial state, otherewise change the checker...
...rest code...
</>
)
}
working example - https://github.com/zero-to-mastery/visual-music/blob/development/src/pages/Login/Login.js
One way to do this is to use the redux state change to decide which component to render. For instance, you can have a property like 'IsLoggedIn' in the redux state and when its 'true' render the '/dashboard' component. something like:
if(IsLoggedIn) return (<Dashboard />)
else return (<Login />)
I have exported multiple variables, but the method I'm using for storing this one does not seem to work for some reason. I have login page, which stores the correct value into "ID" as shown below
import AuthService from './AuthService';
let ID = "";
class LoginPage extends Component {
constructor(props) {
super(props);
this.state = {
username: '',
password: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.Auth = new AuthService();
}
handleFormSubmit(e){
e.preventDefault();
this.Auth.login(this.state.username,this.state.password)
.then(res =>{
if(this.Auth.state.isA)
this.props.history.push('/AdminApp');
else if(this.Auth.state.isA === 0 && this.Auth.state.sub === 0)
{
ID = this.Auth.state.userID;
console.log(ID) // This prints the right value
this.props.history.push('/SDForm')
}
})
.catch(err =>{
alert(err);
})
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
render() {
return (
<Container>
<Col className="UCFLogo"><img src={logo} /></Col>
<Form className="LoginForm">
<Col>
<h1 className="mainTitles">Senior Design Project Selection</h1>
<h3 className="subTitle">Sign In</h3>
</Col>
<Col>
<FormGroup className="LoginPad">
<Label className="subTitle">Knights Email</Label>
<Input className="LoginInfo" type="text" name="username" id="username" onChange={this.handleChange.bind(this)} value={this.state.username} />
</FormGroup>
</Col>
<Col>
<FormGroup>
<Label className="subTitle" for="password">Password</Label>
<Input className="LoginInfo" type="password" name="password" id="password" onChange={this.handleChange.bind(this)} value={this.state.password} />
</FormGroup>
</Col>
<Button className="subTitle" onClick={this.handleFormSubmit}>Submit</Button>
</Form>
</Container>
);
}
}
export default LoginPage;
export {ID};
Then, I need to load that ID from login into my state in my form.js file (below) in order to return it to the json upon submit, I'm just attempting to print the ID to the console until I know that I am getting the right value, and for the sake of length, I cut most of the code out, but I get this in the console
ƒ LoginPage(props) {
var _this;
Object(C_csform_master_node_modules_babel_runtime_helpers_esm_classCallCheck__WEBPACK_IMPORTED_MODULE_1__["default"])(this, LoginPage);
_this = Object(C_cs…
form.js
import ID from './LoginPage';
const Auth = new AuthService();
class SDForm extends Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: "",
ID: "",
}
this.Auth = new AuthService();
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
printToConsole = () => {
console.log(ID)
}
render() {
return (
<Container>
<Form className="SDForm">
// Form stuff
<Col className="subTitle">
<Button onClick={this.printToConsole}>Submit</Button>
</Col>
</Form>
</Container>
);
}
}
export default withAuth(SDForm);
This is not the proper way of passing information between components in React. In most cases, the best way to do it would be putting the value of ID in the Redux store or getting the ID value to them store it on a state and passing the ID state as a prop to the SDForm component, as shown next:
import SDForm from './SDForm.js'
And them (once you get your ID value and you store it on a state):
const { ID } = this.state;
And then in the <SDForm /> you can use ID prop as you see fit.
<SDForm id={ID} />
So I have built a Wizard Form using React-Final-Form that I am using in my sign-up page. I am trying to figure out how I can display all user inputs on the final page as a way for the user to double-check/verify their inputs before submitting. Any help would be greatly appreciated!
(P.S. - I tried researching this before posting, but all I was able to find was storing user inputs in Redux and accessing them from there, which I'd like to avoid, if at all possible.)
Here is an example link that shows what I want to do - Please feel free to fork and play around with it if you are trying to figure out a solution! https://codesandbox.io/s/0332k02x0v
Here is the code, shortened to include only the relevant bits:
My Wizard.js page:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Form } from "react-final-form";
class Wizard extends Component {
static propTypes = {
onSubmit: PropTypes.func.isRequired
};
static Page = ({ children }) => children;
constructor(props) {
super(props);
this.state = {
page: 0,
values: props.initialValues || {}
};
}
next = values =>
this.setState(state => ({
page: Math.min(state.page + 1, this.props.children.length - 1),
values
}));
previous = () =>
this.setState(state => ({
page: Math.max(state.page - 1, 0)
}));
validate = values => {
const activePage = React.Children.toArray(this.props.children)[
this.state.page
];
return activePage.props.validate ? activePage.props.validate(values) : {};
};
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
};
render() {
const { children } = this.props;
const { page, values } = this.state;
const activePage = React.Children.toArray(children)[page];
const isLastPage = page === React.Children.count(children) - 1;
return (
<Form
initialValues={values}
validate={this.validate}
onSubmit={this.handleSubmit}
>
{({ handleSubmit, submitting, values }) => (
<form onSubmit={handleSubmit}>
{activePage}
<div className="buttons">
{page > 0 && (
<button type="button" onClick={this.previous}>
« Previous
</button>
)}
{!isLastPage && <button type="submit">Next »</button>}
{isLastPage && (
<button type="submit" disabled={submitting}>
Submit
</button>
)}
</div>
{/* <pre>{JSON.stringify(values, 0, 2)}</pre> */}
</form>
)}
</Form>
);
}
}
export default Wizard;
My index.js page:
import React, { Component } from "react";
import { Field } from "react-final-form";
import formatString from "format-string-by-pattern";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Wizard from "./Wizard";
import Styles from "./Styles";
import { addUser } from "../../../actions/authActions";
class ReactFinalForm2 extends Component {
state = {};
render() {
const onSubmit = async values => {
this.props.addUser(values);
// API query here
};
const Error = ({ name }) => (
// Error handing here
);
return (
<Styles>
<div>
<Wizard initialValues={{}} onSubmit={onSubmit}>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page>
{/* *** THIS IS WHERE I WOULD LIKE TO DISPLAY ALL PREVIOUS VALUES (SO THE USER CAN CONFIRM / DOUBLE-CHECK THEIR INPUTS BEFORE SUBMITTING) *** */}
</Wizard.Page>
</Wizard>
</div>
</Styles>
);
}
}
ReactFinalForm2.propTypes = {
addUser: PropTypes.func.isRequired
};
export default connect(
null,
{ addUser }
)(ReactFinalForm2);
I have added a state to the parent component. Changing this state on every submit from the child. I have JSON stringify the state in parent component. As you said no need to use redux, this is the workaround I came with. Still your code has a potential for improvements. Please check this working sandbox:
[ https://codesandbox.io/s/zrvloq4o6x ]
Wizard.js change
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
// Change added
this.props.onStateChange(values);
};
Index.js
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Field } from "react-final-form";
import Wizard from "./Wizard";
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const onSubmit = async values => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const Error = ({ name }) => (
<Field
name={name}
subscribe={{ touched: true, error: true }}
render={({ meta: { touched, error } }) =>
touched && error ? <span>{error}</span> : null
}
/>
);
const required = value => (value ? undefined : "Required");
let data = {};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.onStateChange = this.onStateChange.bind(this);
}
onStateChange = values => {
this.setState({ data: values });
console.log(values);
};
render() {
return (
<Styles>
<h1>🏁 React Final Form Example</h1>
<h2>Wizard Form</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
Notice the mixture of field-level and record-level (or{" "}
<em>page-level</em> in this case) validation.
</p>
<Wizard
initialValues={{}}
onStateChange={this.onStateChange}
onSubmit={onSubmit}
>
<Wizard.Page>
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
validate={required}
/>
<Error name="firstName" />
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
validate={required}
/>
<Error name="lastName" />
</div>
</Wizard.Page>
<Wizard.Page
validate={values => {
const errors = {};
if (!values.notes) {
errors.notes = "Required";
}
return errors;
}}
>
<div>
<label>Best Stooge?</label>
<div>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="larry"
/>{" "}
Larry
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="moe"
/>{" "}
Moe
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="curly"
/>{" "}
Curly
</label>
</div>
</div>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
<Error name="notes" />
</div>
</Wizard.Page>
<Wizard.Page>
<div>
<p>
<b>Display all previous values here for user verification </b>
<br />
<i>{JSON.stringify(this.state.data, 0, 2)}</i>
</p>
</div>
</Wizard.Page>
</Wizard>
</Styles>
);
}
}
render(<App />, document.getElementById("root"));
My problem is simple, at least it seems. I have state in my redux store that's holding the state of whether the user is logged in or not. That is all working fine, but when the user refreshes the page, for a moment while the authenticated state is async getting it's data, the render runs and state is undefined.
Since, state is undefined, my redirect to /login runs, so the refresh kicks me out of the app and back to login, which then checks to see if I'm already logged in and take me to the homepage.
Any ideas on how to resolve this:
{
!this.props.authenticated && (
<Switch>
<Route path="/login" component={LoginForm} />
<Route path="/register" component={RegisterForm} />
<Route path="" render={props => {
return <Redirect to="/login" />
}}
/>
</Switch>
)
}
So, when this.props.authenticated is false for that short period of time, it hits the login redirect. But, a few ms later, this.props.authenticated is true and since the user is already logged in, is redirected to the home route.
Any ideas?
Ideally you wouldn't render your route straight away but wait until your authentication request has resolved and you have a clear state.
Something like this:
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
// You could just check if authenticated is null,
// but I think having an extra state makes is more clear
isLoading: true,
authenticated: null,
};
this.checkAuthentication();
}
checkAuthentication() {
// Some async stuff checking against server
// I’ll simulate an async call with this setTimeout
setTimeout(
() => this.setState( {
authenticated: Boolean( Math.round( Math.random() ) ),
isLoading: false,
} ),
1000
);
}
render() {
// Render a loading screen when we don't have a clear state yet
if ( this.state.isLoading ) {
return <div>loading</div>;
}
// Otherwise it is safe to render our routes
return (
<div>
routes,<br />
random authenticated:
<strong>
{ this.state.authenticated.toString() }
</strong>
</div>
);
}
}
ReactDOM.render( (
<App />
), document.querySelector( 'main' ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main></main>
First of all when user try to login then you receive token in response when user is authenticated. now you have to store token in localStorage using
if(user.token){
localStorage.setItem('user', JSON.stringify(user));
}
it is indicated that when you have token in localstorage you are login otherwise you are logout.
now try to set state to redirect to home page if you want to go home page after login.
this.setState({redirectToReferrer: true});
now return redirect to desire page
if (this.state.redirectToReferrer){
return (<Redirect to={'/home'}/>)
}
login.js
import React from 'react';
import axios from 'axios';
import {Redirect} from 'react-router-dom';
export default class Login extends React.Component{
constructor(props){
super(props);
this.state = {
email : '' ,
password : '',
redirectToReferrer : false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event){
this.setState({
[event.target.name] : event.target.value
});
}
handleSubmit(event){
event.preventDefault();
const user = {
email : this.state.email,
password : this.state.password
};
if(this.state.email && this.state.password)
{
axios.post(`{Api}/login`,user)
.then((response) =>
{
let userresponse = response;
console.log(userresponse.data);
if(userresponse.token){
sessionStorage.setItem('data',JSON.stringify(userresponse));
this.setState({redirectToReferrer: true});
}
},this)
.catch((error) => alert(error))
}
}
render(){
if (this.state.redirectToReferrer){
return (<Redirect to={'/user'}/>)
}
if (sessionStorage.getItem('data')){
return (<Redirect to={'/user'}/>)
}
return(
<div>
<form ref="formdemo" onSubmit={this.handleSubmit}>
<label>
Username:
<input type="email" name="email" onChange={this.handleChange} placeholder="Enter Your EmailID" required/></label><br/>
<label>
Password :
<input type="password" name="password" onChange={this.handleChange} placeholder="Enter Your Password" required/></label><br/>
<input type="submit"/>
</form>
</div>
)
}
}
Okay, lumio helped get me on the right track with setTimeout, so instead I worked it out with async/await:
class App extends Component {
state = {
error: "",
isLoading: true,
}
async componentDidMount() {
let token = localStorage.getItem('jwtToken');
if (token) {
setAuthToken(token);
await this.props.isAuthenticatedAction(true);
} else {
await this.props.isAuthenticatedAction(false);
}
this.setState({
isLoading: false,
});
}
handleLogout = (evt) => {
evt.preventDefault();
localStorage.removeItem('jwtToken');
window.location.reload();
}
render() {
if (this.state.isLoading) {
return <div></div>;
} else {
// return my regular content
}
You can use react-router-dom for auth work flow.
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
////////////////////////////////////////////////////////////
// 1. Click the public page
// 2. Click the protected page
// 3. Log in
// 4. Click the back button, note the URL each time
const AuthExample = () => (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Route path="/public" component={Public} />
<Route path="/login" component={Login} />
<PrivateRoute path="/protected" component={Protected} />
</div>
</Router>
);
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
}
};
const AuthButton = withRouter(
({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
const Public = () => <h3>Public</h3>;
const Protected = () => <h3>Protected</h3>;
class Login extends React.Component {
state = {
redirectToReferrer: false
};
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return <Redirect to={from} />;
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
);
}
}
export default AuthExample;
refer link https://reacttraining.com/react-router/web/example/auth-workflow