Redirection In Some Cases Only - javascript

I have implemented Private Routing like this. If a token is present in the localStorage, private routes can be accessed. If not, we should redirect to the /404 page:
const token = localStorage.getItem('token');
const PrivateRoute = ({component, isAuthenticated, ...rest}: any) => {
const routeComponent = (props: any) => (
isAuthenticated
? React.createElement(component, props)
: <Redirect to={{pathname: '/404'}}/>
);
return <Route {...rest} render={routeComponent}/>;
};
But I am also using redirection in my LoginPage.
I want that if login is unsuccessful, the error message from ShowError() is shown. Instead of redirecting. This was working properly before I added the redirection. If the login is successful, I should go to /panel. However, right now, if the login is unsuccessful, I just go to /404. Instead of showing the error message. How can I fix this?
Login Screen:
function LoginPage (){
const [state, setState] = useState({
email: '',
password: '',
});
const [submitted, setSubmitted] = useState(false);
function ShowError(){
if (!localStorage.getItem('token'))
{
console.log('Login Not Successful');
return (
<ThemeProvider theme={colortheme}>
<Typography color='primary'>
Login Unsuccessful
</Typography>
</ThemeProvider>)
}
}
function FormSubmitted(){
setSubmitted(true);
}
function RedirectionToPanel(){
if(submitted && localStorage.getItem('token')){
return <Redirect to='/panel'/>
}
}
function submitForm(LoginMutation: any) {
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
})
.catch(console.log)
}
}
return (
<Mutation mutation={LoginMutation}>
{(LoginMutation: any) => (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}>
<Avatar>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<Formik
initialValues={{ email: '', password: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email, password },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
setState( prevState => ({ ...prevState, [name]: e.target.value }));
};
return (
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(LoginMutation);FormSubmitted();RedirectionToPanel()}}>
<TextField
variant="outlined"
margin="normal"
id="email"
fullWidth
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="password"
name="password"
helperText={touched.password ? errors.password : ""}
error={touched.password && Boolean(errors.password)}
label="Password"
type="password"
value={password}
onChange={change.bind(null, "password")}
/>
{submitted && ShowError()}
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<br />
<Button className='button-center'
type="submit"
disabled={!isValid || !email || !password}
// onClick={handleOpen}
style={{
background: '#6c74cc',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px'
}}
>
Submit</Button>
<br></br>
<Grid container>
<Grid item xs>
</form>
)
}}
</Formik>
</div>
{submitted && <Redirect to='/panel'/>}
</Container>
)
}
</Mutation>
);
}
export default LoginPage;
Edit::
I tried using this too:
if(localStorage.getItem('token')){
return <Redirect to='/panel'/>
}
});
but it gives me an error on the arrow that:
Argument of type '() => JSX.Element | undefined' is not assignable to parameter of type 'EffectCallback'.
Type 'Element | undefined' is not assignable to type 'void | (() => void | undefined)'.
Type 'Element' is not assignable to type 'void | (() => void | undefined)'.
Type 'Element' is not assignable to type '() => void | undefined'.
Type 'Element' provides no match for the signature '(): void | undefined'.ts(2345)

remove this line at the end of the container.
{submitted && <Redirect to='/panel'/>}
as it doesn't make sense to be there.
also, adjust the submit method to execute functions correctly.
function submitForm(LoginMutation: any) {
const { email, password } = state;
return LoginMutation({
variables: {
email,
password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
}).catch(console.error);
}
with promise being returned from the submitForm method, you can do the following.
onSubmit={e => {
e.preventDefault();
submitForm(LoginMutation).then((res: any) => {
RedirectionToPanel();
FormSubmitted();
});
}}

Related

changing routes when once user signed in using oauth firebase in react app

i need to change when users sign up the sign up button in top right changes to profile. i need to do the same thing using oath using google and facebook with firebase authentication. the state is maintained in redux. i have attached the code for the signin component, signup component, reducer, store
const Form = (props) => {
const classes = useStyles();
const [formState, setFormState] = React.useState({
isValid: false,
values: {},
touched: {},
errors: {},
});
React.useEffect(() => {
const errors = validate(formState.values, schema);
setFormState(formState => ({
...formState,
isValid: errors ? false : true,
errors: errors || {},
}));
}, [formState.values]);
const handleChange = event => {
event.persist();
setFormState(formState => ({
...formState,
values: {
...formState.values,
[event.target.name]:
event.target.type === 'checkbox'
? event.target.checked
: event.target.value,
},
touched: {
...formState.touched,
[event.target.name]: true,
},
}));
};
const handleSubmit = event => {
event.preventDefault();
const { name, mobile, email, password } = formState.values
const db = firebase.firestore()
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(data => {
var user = firebase.auth().currentUser;
var data = {
uid: user.uid,
name: name,
email: email,
mobile: mobile,
}
let userRef = db.collection('users').doc(user.uid)
userRef.set(data).then(_ => {
let enqRef = db.collection('enquires').doc(user.uid)
enqRef.set({enquiries: []}).then(_ => {
props.history.push('/')
window.location.reload()
})
})
})
.catch(error =>{
alert(error['message'])
})
setFormState(formState => ({
...formState,
touched: {
...formState.touched,
...formState.errors,
},
}));
};
const hasError = field => formState.touched[field] && formState.errors[field] ? true : false;
return (
<div className={classes.root}>
<form name="password-reset-form" method="post" onSubmit={handleSubmit}>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
placeholder="Name"
label="Name *"
variant="outlined"
size="medium"
name="name"
fullWidth
helperText={
hasError('name') ? formState.errors.name[0] : null
}
error={hasError('name')}
onChange={handleChange}
type="text"
value={formState.values.name || ''}
/>
</Grid>
<Grid item xs={12}>
<TextField
placeholder="Mobile Number"
label="Mobile Number *"
variant="outlined"
size="medium"
name="mobile"
fullWidth
helperText={hasError('mobile') ? formState.errors.mobile[0] : null}
error={hasError('mobile')}
onChange={handleChange}
type="number"
value={formState.values.mobile || ''}
/>
</Grid>
<Grid item xs={12}>
<TextField
placeholder="E-mail"
label="E-mail *"
variant="outlined"
size="medium"
name="email"
fullWidth
helperText={hasError('email') ? formState.errors.email[0] : null}
error={hasError('email')}
onChange={handleChange}
type="email"
value={formState.values.email || ''}
/>
</Grid>
<Grid item xs={12}>
<TextField
placeholder="Password"
label="Password *"
variant="outlined"
size="medium"
name="password"
fullWidth
helperText={
hasError('password') ? formState.errors.password[0] : null
}
error={hasError('password')}
onChange={handleChange}
type="password"
value={formState.values.password || ''}
/>
</Grid>
<Grid item xs={12}>
<Button
size="large"
variant="contained"
type="submit"
color="primary"
fullWidth
>
Send
</Button>
</Grid>
<Grid item xs={12}>
<Typography
variant="subtitle1"
color="textSecondary"
align="center"
style={{ display: "flex", justifyContent: "center" }}
>
Already have an account?{' '}
<div color="secondary" onClick={()=> props.history.push("/signIn")} style={{ cursor: "pointer", marginLeft: 5, color: "blue" }}>Sign in</div>
</Typography>
</Grid>
</Grid>
</form>
</div>
);
};
export default withRouter(Form);
this the signup component this component and function works great. i need to do the same functionality in sign in using oauth. here once users enter the data in the text field it changes to profile component
const schema = {
email: {
presence: { allowEmpty: false, message: 'is required' },
email: true,
length: {
maximum: 300,
},
},
password: {
presence: { allowEmpty: false, message: 'is required' },
length: {
minimum: 8,
},
},
};
const Form = ({ history }, props) => {
const classes = useStyles();
const userSignedIn = useSelector((state)=> state.initFunction.userSignedIn);
const [formState, setFormState] = React.useState({
isValid: false,
values: {},
touched: {},
errors: {},
});
React.useEffect(() => {
const errors = validate(formState.values, schema);
setFormState(formState => ({
...formState,
isValid: errors ? false : true,
errors: errors || {},
}));
}, [formState.values]);
const googleLogin = (event)=> {
event.preventDefault();
const authProvider = new firebase.auth.GoogleAuthProvider()
firebase.auth().signInWithPopup(authProvider)
const { name, mobile, email, password } = formState.values
const db = firebase.firestore()
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(data => {
var user = firebase.auth().currentUser;
var data = {
uid: user.uid,
email: data.additionalUserInfo.profile.email,
name:data.additionalUserInfo.profile.name,
}
let userRef = db.collection('users').doc(user.uid)
userRef.set(data).then(_ => {
let enqRef = db.collection('enquires').doc(user.uid)
enqRef.set({enquiries: []}).then(_ => {
props.history.push('/')
window.location.reload()
})
})
})
}
const facebookLogin = async () => {
const provider = new firebase.auth.FacebookAuthProvider();
await firebase.auth().signInWithPopup(provider)
.then((jk)=> {
console.log(jk)
})
.catch((err)=> {
console.log(err)
})
}
const handleChange = event => {
event.persist();
setFormState(formState => ({
...formState,
values: {
...formState.values,
[event.target.name]:
event.target.type === 'checkbox'
? event.target.checked
: event.target.value,
},
touched: {
...formState.touched,
[event.target.name]: true,
},
}));
};
const handleSubmit = event => {
event.preventDefault();
if (formState.isValid) {
const { email, password } = formState.values
firebase.auth().signInWithEmailAndPassword(email, password)
.then(user => {
if(user) {
window.history.back()
// window.location.reload()
}
})
}
setFormState(formState => ({
...formState,
touched: {
...formState.touched,
...formState.errors,
},
}));
};
const hasError = field =>
formState.touched[field] && formState.errors[field] ? true : false;
return (
<div className={classes.root}>
<form name="password-reset-form" method="post" onSubmit={handleSubmit} >
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
placeholder="E-mail"
label="E-mail *"
variant="outlined"
size="medium"
name="email"
fullWidth
helperText={hasError('email') ? formState.errors.email[0] : null}
error={hasError('email')}
onChange={handleChange}
type="email"
value={formState.values.email || ''}
/>
</Grid>
<Grid item xs={12}>
<TextField
placeholder="Password"
label="Password *"
variant="outlined"
size="medium"
name="password"
fullWidth
helperText={
hasError('password') ? formState.errors.password[0] : null
}
error={hasError('password')}
onChange={handleChange}
type="password"
value={formState.values.password || ''}
/>
</Grid>
<Grid item xs={12}>
<Button
size="large"
variant="contained"
type="submit"
color="primary"
fullWidth
>
Sign In
</Button>
</Grid>
<Grid item xs={12}>
<Button
size="large"
variant="outlined"
color="primary"
fullWidth
onClick={googleLogin}
startIcon={<img style={{ height: 20, width: 20 }} src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/1200px-Google_%22G%22_Logo.svg.png" />}
>
Sign In with Google
</Button>
</Grid>
<Grid item xs={12}>
<Button
size="large"
variant="outlined"
color="primary"
fullWidth
onClick ={ facebookLogin}
startIcon={<img style={{ height: 20, width: 20 }} src="https://www.freepnglogos.com/uploads/facebook-logo-icon/facebook-logo-icon-file-facebook-icon-svg-wikimedia-commons-4.png" />}
>
Sign In with Facebook
</Button>
</Grid>
<Grid item xs={14}>
<Button
style ={{ paddingLeft: "10px"}}
variant="subtitle1"
color="textSecondary"
onClick = {()=> history.push("/passwordreset")}
style ={{
}}
>
Forgot your password?
</Button>
</Grid>
</Grid>
</form>
</div>
);
};
export default withRouter(Form)
this is for signin component. i need to have the functionality when users click on google button. this is the action creator
export async function checkUserlogIn(dispatch, getState){
firebase.auth().onAuthStateChanged(user => {
if(user) {
const db = firebase.firestore()
let userDocRef = db.collection('users').doc(user.uid)
userDocRef.get()
.then(doc => {
let enqRef = db.collection('enquires').doc(user.uid)
let pData = doc.data()
pData.uid = doc.id
enqRef.get()
.then(enqData => {
pData.enquiries = enqData.data()['enquiries']
dispatch({ type: "user_signed_in", payload: { "signIn": true, "pData": [ pData ]}})
})
})
}
})
}
the reducer function
const initialState = {
loading: true,
switchConstructionMode: false,
displayProductPage: false,
userSignedIn: false,
prdCategoryData: [],
aboutPage: {},
solnCatData: [], //
pData: {},
landingPageContent : {},
solutions : [],
solutionName : [],
selectedCategoryIndex : 0,
filteredSolutionProducts : [],
googleLoading: false,
currentUser: null,
error: null
function initFunction(state = initialState, action){
var type = action.type;
var payload = action.payload
if(type=="user_signed_in"){
state.userSignedIn = payload.signIn
state.pData = payload.pData[0]
return state;
i am getting this error.
i'm new to react.
i have tried to push the route to the home component that is working but i am not able to change the sign button to firebase. and i am not able to to add the collection in the firebase

Redirection with useEffect() Not Working?

If the login is successful and a token is stored in localStorage, I should be redirected to a private route i.e /panel. If not, I should show the error message from ShowError().
Currently, my error message is being displayed if the login is not successful so it's all good. However, if there is a token present, there is still no redirection.
function LoginPage (){
const [state, setState] = useState({
email: '',
password: '',
});
const [submitted, setSubmitted] = useState(false);
const [shouldRedirect, setShouldRedirect] = useState(false);
function ShowError(){
if (!localStorage.getItem('token'))
{
console.log('Login Not Successful');
return (
<ThemeProvider theme={colortheme}>
<Typography color='primary'>
Login Unsuccessful
</Typography>
</ThemeProvider>)
}
}
// function FormSubmitted(){
// setSubmitted(true);
// console.log('Form submitted');
// }
// function RedirectionToPanel(){
// console.log('check');
// if(submitted && localStorage.getItem('token')){
// console.log('Finall');
// return <Redirect to='/panel'/>
// }
// }
useEffect(() => {
if(localStorage.getItem('token')){
setShouldRedirect(true);
}
},[] );
function submitForm(LoginMutation: any) {
setSubmitted(true);
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
})
.catch(console.log)
}
}
return (
<Mutation mutation={LoginMutation}>
{(LoginMutation: any) => (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}>
<Avatar>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<Formik
initialValues={{ email: '', password: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email, password },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
setState( prevState => ({ ...prevState, [name]: e.target.value }));
};
return (
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(LoginMutation);}}>
<TextField
variant="outlined"
margin="normal"
id="email"
fullWidth
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="password"
name="password"
helperText={touched.password ? errors.password : ""}
error={touched.password && Boolean(errors.password)}
label="Password"
type="password"
value={password}
onChange={change.bind(null, "password")}
/>
{submitted && ShowError()}
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<br />
<Button className='button-center'
type="submit"
disabled={!isValid || !email || !password}
// onClick={handleOpen}
style={{
background: '#6c74cc',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px'
}}
>
Submit</Button>
<br></br>
<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</form>
)
}}
</Formik>
</div>
<Box mt={8}>
<Copyright />
</Box>
{/* {submitted && <Redirect to='/panel'/>} */}
</Container>
)
}
</Mutation>
);
}
export default LoginPage;
My App.tsx:
const token = localStorage.getItem('token');
const PrivateRoute = ({component, isAuthenticated, ...rest}: any) => {
const routeComponent = (props: any) => (
isAuthenticated
? React.createElement(component, props)
: <Redirect to={{pathname: '/404'}}/>
);
return <Route {...rest} render={routeComponent}/>;
};
export default function App() {
return (
<div>
<BrowserRouter>
<Switch>
<Route exact path='/' component= {HomePage}></Route>
<Route path='/login' component= {LoginPage}></Route>
<Route path='/404' component= {Error404Page}></Route>
<PrivateRoute
path='/panel'
isAuthenticated={token}
component={PanelHomePage}
/>
<Redirect from='*' to='/404' />
</Switch>
</BrowserRouter>
</div>
);
}
Why do you use useState to redirect? Just redirect directly. And if you are using react-router it is good to use useHistory.
import { useHistory } from 'react-router-dom';
const history = useHistory();
useEffect(() => {
if(localStorage.getItem('token')){
history.push({
pathname: '/',
});
}
},[]);
If you still want to use useState you can create a new useEffect NS add that state into your dependency array.
const history = useHistory();
useEffect(() => {
if(localStorage.getItem('token')){
setShouldRedirect(true);
}
},[]);
useEffect(() => {
if(shouldRedirect){
history.push({
pathname: '/',
});
}
},[shouldRedirect]);

UseEffect() keeps giving error for Redirection

If login is successful, a token is returned and is stored in the local storage. In this case, I should redirect to a private route /panel. If login is unsuccessful, I should be able to show the error message from ShowError(). The error message functionality was working properly before I added the redirection.
This is my LoginPage.tsx
function LoginPage (){
const [state, setState] = useState({
email: '',
password: '',
});
const [submitted, setSubmitted] = useState(false);
function ShowError(){
if (!localStorage.getItem('token'))
{
console.log('Login Not Successful');
return (
<ThemeProvider theme={colortheme}>
<Typography color='primary'>
Login Unsuccessful
</Typography>
</ThemeProvider>)
}
}
function FormSubmitted(){
setSubmitted(true);
console.log('Form submitted');
}
function RedirectionToPanel(){
if(submitted && localStorage.getItem('token')){
return <Redirect to='/panel'/>
}
}
// useEffect(() => {
// if(localStorage.getItem('token')){
// return <Redirect to='/panel'/>
// }
// },[] );
function submitForm(LoginMutation: any) {
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
})
.catch(console.log)
}
}
return (
<Mutation mutation={LoginMutation}>
{(LoginMutation: any) => (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}>
<Avatar>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<Formik
initialValues={{ email: '', password: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email, password },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
setState( prevState => ({ ...prevState, [name]: e.target.value }));
};
return (
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(LoginMutation);FormSubmitted();RedirectionToPanel()}}>
<TextField
variant="outlined"
margin="normal"
id="email"
fullWidth
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="password"
name="password"
helperText={touched.password ? errors.password : ""}
error={touched.password && Boolean(errors.password)}
label="Password"
type="password"
value={password}
onChange={change.bind(null, "password")}
/>
{submitted && ShowError()}
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<br />
<Button className='button-center'
type="submit"
disabled={!isValid || !email || !password}
// onClick={handleOpen}
style={{
background: '#6c74cc',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px'
}}
>
Submit</Button>
<br></br>
<Grid container>
<Grid item xs>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</form>
)
}}
</Formik>
</div>
{submitted && <Redirect to='/panel'/>}
</Container>
)
}
</Mutation>
);
}
export default LoginPage;
Currently, if the login is unsuccessful, it automatically redirects to page /404. However, I want it to direct to /404 only if an invalid/private link is entered. In case of unsuccessful login, I want to show the error message.
I also tried to use UseEffect(), which is commented out in the code above but it keeps giving me this error on the arrow:
Argument of type '() => JSX.Element | undefined' is not assignable to parameter of type 'EffectCallback'.
Type 'Element | undefined' is not assignable to type 'void | (() => void | undefined)'.
Type 'Element' is not assignable to type 'void | (() => void | undefined)'.
Type 'Element' is not assignable to type '() => void | undefined'.
Type 'Element' provides no match for the signature '(): void | undefined'.ts(2345)
here is how my private routing is implemented in the App.tsx
const token = localStorage.getItem('token');
const PrivateRoute = ({component, isAuthenticated, ...rest}: any) => {
const routeComponent = (props: any) => (
isAuthenticated
? React.createElement(component, props)
: <Redirect to={{pathname: '/404'}}/>
);
return <Route {...rest} render={routeComponent}/>;
};
If I comment out the {submitted && <Redirect to='/panel'/>}, I get the error message on unsuccessful login but then there's no redirection even when the login is successful.
Edited Code:
function LoginPage (){
const [state, setState] = useState({
email: '',
password: '',
});
const [submitted, setSubmitted] = useState(false);
const [shouldRedirect, setShouldRedirect] = useState(false);
function ShowError(){
if (!localStorage.getItem('token'))
{
console.log('Login Not Successful');
return (
<ThemeProvider theme={colortheme}>
<Typography color='primary'>
Login Unsuccessful
</Typography>
</ThemeProvider>)
}
}
// function FormSubmitted(){
// setSubmitted(true);
// console.log('Form submitted');
// }
function RedirectionToPanel(){
console.log('check');
if(submitted && localStorage.getItem('token')){
console.log('Finall');
return <Redirect to='/panel'/>
}
}
useEffect(() => {
if(localStorage.getItem('token')){
setShouldRedirect(true);
}
},[] );
function submitForm(LoginMutation: any) {
setSubmitted(true);
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
})
.catch(console.log)
}
}
return (
<Mutation mutation={LoginMutation}>
{(LoginMutation: any) => (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}>
<Avatar>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<Formik
initialValues={{ email: '', password: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
validationSchema={schema}
>
{props => {
const {
values: { email, password },
errors,
touched,
handleChange,
isValid,
setFieldTouched
} = props;
const change = (name: string, e: any) => {
e.persist();
handleChange(e);
setFieldTouched(name, true, false);
setState( prevState => ({ ...prevState, [name]: e.target.value }));
};
return (
<form style={{ width: '100%' }}
onSubmit={e => {e.preventDefault();
submitForm(LoginMutation);RedirectionToPanel()}}>
<TextField
variant="outlined"
margin="normal"
id="email"
fullWidth
name="email"
helperText={touched.email ? errors.email : ""}
error={touched.email && Boolean(errors.email)}
label="Email"
value={email}
onChange={change.bind(null, "email")}
/>
<TextField
variant="outlined"
margin="normal"
fullWidth
id="password"
name="password"
helperText={touched.password ? errors.password : ""}
error={touched.password && Boolean(errors.password)}
label="Password"
type="password"
value={password}
onChange={change.bind(null, "password")}
/>
{submitted && ShowError()}
<FormControlLabel
control={<Checkbox value="remember" color="primary" />}
label="Remember me"
/>
<br />
<Button className='button-center'
type="submit"
disabled={!isValid || !email || !password}
// onClick={handleOpen}
style={{
background: '#6c74cc',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px'
}}
>
Submit</Button>
</form>
)
}}
</Formik>
</div>
{/* {submitted && <Redirect to='/panel'/>} */}
</Container>
)
}
</Mutation>
);
}
export default LoginPage;
I believe that returning Redirect component shouldn't be called in useEffect hook or inside any function.
You should have something like that:
function LoginPage (){
const [state, setState] = useState({
email: '',
password: '',
});
const [shouldRedirect, setShouldRedirect] = useState(false);
useEffect(() => {
if(localStorage.getItem("token")) {
setShouldRedirect(true);
}
}, []);
function submitForm(LoginMutation: any) {
setSubmitted(true);
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
setShouldRedirect(true);
})
.catch(console.log)
}
}
if(shouldRedirect) return <Redirect to="/panel" />;
return (
... rest of code
);
}

How to do internal redirecting in reactjs?

I'm kinda new to react and was wondering how to internally redirect your pages in reactjs. I have two pages called register and register2. In register page, I just check if the email exists in the database or not and if it doesn't exist then it redirects to register2 page for creating the full account with username and password. However, in the address bar, it kinda looks ugly to show something like register2. So I was wondering if there is any way through which I can internally redirect without changing the address in the address bar to register2 such that it stays as register throughout the whole account creation process.
I created a codesandbox to show the issue
register.js
import React, { useState } from "react";
import { Formik } from "formik";
import TextField from "#material-ui/core/TextField";
import * as Yup from "yup";
import Avatar from "#material-ui/core/Avatar";
import Button from "#material-ui/core/Button";
import CssBaseline from "#material-ui/core/CssBaseline";
import Link from "#material-ui/core/Link";
import Grid from "#material-ui/core/Grid";
import Box from "#material-ui/core/Box";
import LockOutlinedIcon from "#material-ui/icons/LockOutlined";
import Typography from "#material-ui/core/Typography";
import { makeStyles } from "#material-ui/core/styles";
import Container from "#material-ui/core/Container";
function Copyright() {
return (
<Typography variant="body2" color="textSecondary" align="center">
<Link color="inherit" href="sad">
New1
</Link>{" "}
{new Date().getFullYear()}
{"."}
</Typography>
);
}
const useStyles = makeStyles(theme => ({
paper: {
marginTop: theme.spacing(8),
display: "flex",
flexDirection: "column",
alignItems: "center"
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main
},
form: {
width: "100%",
marginTop: theme.spacing(1)
},
submit: {
margin: theme.spacing(3, 0, 2)
}
}));
const Reg = props => {
const classes = useStyles();
const [loginError, setLoginError] = useState("");
const [changed, setChanged] = useState(false);
const [newpage, setNew] = useState(false);
const handleSubmit = async (values, { setSubmitting }) => {
const { email } = values;
var body = {
email: email
};
console.log(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/emailcheck";
try {
const response = await fetch(url, options);
const text = await response.text();
setSubmitting(false);
setChanged(false);
setNew(true);
console.log(text);
if (newpage) {
props.history.push({
pathname: "/register2",
state: { email }
});
// props.history.push(`/register2/${email}`);
} else if (text === "exists") {
props.history.push(`/`);
} else {
setLoginError("Email is invalid");
}
} catch (error) {
console.error(error);
}
};
return (
<Formik
initialValues={{ email: "" }}
onSubmit={handleSubmit}
//********Using Yup for validation********/
validationSchema={Yup.object().shape({
email: Yup.string()
.email()
.required("Required")
})}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props;
return (
<>
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form
className={classes.form}
onSubmit={handleSubmit}
noValidate
>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="email"
value={values.email}
label="Email Address"
name="email"
autoComplete="email"
onChange={e => {
setChanged(true);
handleChange(e);
}}
onBlur={handleBlur}
className={errors.email && touched.email && "error"}
/>
{errors.email && touched.email && (
<div className="input-feedback" style={{ color: "red" }}>
{errors.email}
</div>
)}
{!changed && loginError && (
<div style={{ color: "red" }}>
<span>{loginError}</span>
</div>
)}
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
disabled={isSubmitting}
>
Next
</Button>
<Grid container justify="flex-end">
<Grid item>
<Link href="/" variant="body2">
Already have an account? Sign in
</Link>
</Grid>
</Grid>
</form>
</div>
<Box mt={5}>
<Copyright />
</Box>
</Container>
</>
);
}}
</Formik>
);
};
export default Reg;
register2.js
import React, { useState } from "react";
import { Formik } from "formik";
import TextField from "#material-ui/core/TextField";
import { withRouter, useHistory } from "react-router-dom";
import * as Yup from "yup";
import Avatar from "#material-ui/core/Avatar";
import Button from "#material-ui/core/Button";
import CssBaseline from "#material-ui/core/CssBaseline";
import Link from "#material-ui/core/Link";
import Box from "#material-ui/core/Box";
import LockOutlinedIcon from "#material-ui/icons/LockOutlined";
import Typography from "#material-ui/core/Typography";
import { makeStyles } from "#material-ui/core/styles";
import Container from "#material-ui/core/Container";
function Copyright() {
return (
<Typography va riant="body2" color="textSecondary" align="center">
<Link color="inherit" href="sad">
New
</Link>{" "}
{new Date().getFullYear()}
{"."}
</Typography>
);
}
const useStyles = makeStyles(theme => ({
paper: {
marginTop: theme.spacing(8),
display: "flex",
flexDirection: "column",
alignItems: "center"
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main
},
form: {
width: "100%",
marginTop: theme.spacing(1)
},
submit: {
margin: theme.spacing(3, 0, 2)
}
}));
const Reg2 = props => {
const classes = useStyles();
const [loginError, setLoginError] = useState("");
const history = useHistory();
const [changed, setChanged] = useState(false);
const handleSubmit = async (values, { setSubmitting }) => {
const { username, password } = values;
var body = {
username: username,
password: password,
email: history.location.state.email
};
console.log(body);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
body: JSON.stringify(body)
};
const url = "/api/register";
try {
const response = await fetch(url, options);
const text = await response.text();
setSubmitting(false);
setChanged(false);
console.log(text);
if (text === "verifyemail") {
props.history.push({
pathname: "/verifyOtp",
state: { email: body.email }
});
// props.history.push(`/verifyOtp/${username}`);
} else {
setLoginError("Username or Password is incorrect");
}
} catch (error) {
console.error(error);
}
};
return (
<Formik
initialValues={{ username: "", password: "", confirmPassword: "" }}
onSubmit={handleSubmit}
//********Using Yup for validation********/
validationSchema={Yup.object().shape({
username: Yup.string().required("Required"),
password: Yup.string()
.required("No password provided.")
.min(8, "Password is too short - should be 8 chars minimum.")
.matches(/(?=.*[0-9])/, "Password must contain a number.")
.matches(
/(?=.*[●!"#$%&'()*+,\-./:;<=>?#[\\\]^_`{|}~])/,
"Password must contain a symbol."
),
confirmPassword: Yup.string()
.required("Enter to confirm password")
.oneOf([Yup.ref("password"), null], "Password do not match")
})}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props;
return (
<>
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Enter Info
</Typography>
<form
className={classes.form}
onSubmit={handleSubmit}
noValidate
>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="username"
value={values.username}
label="username"
name="username"
autoComplete="username"
onChange={e => {
setChanged(true);
handleChange(e);
}}
onBlur={handleBlur}
className={errors.username && touched.username && "error"}
/>
{errors.username && touched.username && (
<div className="input-feedback" style={{ color: "red" }}>
{errors.username}
</div>
)}
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="password"
value={values.password}
label="Password"
type="password"
id="password"
onBlur={handleBlur}
autoComplete="current-password"
className={errors.password && touched.password && "error"}
onChange={e => {
setChanged(true);
handleChange(e);
}}
/>
{errors.password && touched.password && (
<div className="input-feedback" style={{ color: "red" }}>
{errors.password}
</div>
)}
<TextField
variant="outlined"
margin="normal"
required
fullWidth
name="confirmPassword"
value={values.confirmPassword}
type="password"
label="Confirm Password"
id="confirmPassword"
onBlur={handleBlur}
autoComplete="confirmPassword"
className={
errors.confirmPassword &&
touched.confirmPassword &&
"error"
}
onChange={e => {
setChanged(true);
handleChange(e);
}}
/>
{errors.confirmPassword && touched.confirmPassword && (
<div className="input-feedback" style={{ color: "red" }}>
{errors.confirmPassword}
</div>
)}
{!changed && loginError && (
<div style={{ color: "red" }}>
<span>{loginError}</span>
</div>
)}
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
disabled={isSubmitting}
>
Next
</Button>
</form>
</div>
<Box mt={8}>
<Copyright />
</Box>
</Container>
</>
);
}}
</Formik>
);
};
export default withRouter(Reg2);
you could make a parent component for register and register2 and store your logic there in which component to display
function MyComponent() {
const [email, setEmail] = useState(null)
useEffect(() => {
(async () => {
const response = await yourApiCall()
setEmail(response)
})()
}, [])
return email ? <Register /> : <Register2 />
}
Initially show the register component or code of that sort.
Call an api, if the email exists, store flag value in that and do conditional rendering for another component or code of register2.
Initially, this.state.isEmailExist : false,
After api call change flag value accordingly. If true then render Register2.
const contents = if (!this.state.isEmailExist) {
return (
<Register />
);
} else {
return (
<Register2 />
)
}
return(
<div>{contents}</div>
)
Maybe you are looking for this
window.open("https://www.youraddress.com","_self");
Make sure to setup the validation.

redux form data is not preserved

I have used redux-form for the form. I have developed a wizard considering an example from the documentation. The wizard is working but when i go to step 2 i.e SyncAccount component which has the form and fill that form and hit the next button to go to another step and hit the back button to go back to the SyncAccount component then the form is cleared. I have used destroyOnUnmount but it is not solving my issue either. What have i missed?
only second page i.e SyncAccount has only form. First and third do not have.
here is the code
AccountSteps.js
const mapDispatchToProps = dispatch => ({
getUser: () => dispatch(getCurrentUser()),
postWizard: (userObj, wizardType) =>
dispatch(postWizardData(userObj, wizardType)),
updateWizard: (userObj, wizardType) =>
dispatch(updateWizardData(userObj, wizardType)),
});
const mapStateToProps = state => ({
userData: state.userReducer,
wizard: state.wizardReducer,
});
class AccountSteps extends React.Component<{
user: Object,
wizard: Object,
postWizard: Function,
updateWizard: Function,
getUser: Function
}> {
constructor(props) {
super(props);
this.state = {
page: 0,
steps: [
{ steps: 0, label: 'Privacy' },
{ steps: 1, label: 'Sync Your Account' },
{ steps: 2, label: 'Install Extension' },
],
};
}
componentDidMount() {
this.props.getUser();
}
static getDerivedStateFromProps(nextProps, prevState) {
const { wizard } = nextProps;
if (!isEmpty(wizard) && wizard.page !== prevState.page) {
return {
page: wizard.page,
};
}
return null;
}
nextPage = (userObj, type) => {
this.props.postWizard(userObj, type);
};
previousPage = (wizardData) => {
console.log('wizardData', wizardData);
this.props.updateWizard(wizardData);
};
render() {
const { page, steps } = this.state;
return (
<Wrapper>
<CardWrapper>
<Stepper activeStep={page} alternativeLabel>
{steps.map(step => (
<Step key={step.steps}>
<StepLabel>{step.label}</StepLabel>
</Step>
))}
</Stepper>
{page === 0 && (
<Privacy
{...this.props}
activeStep={page}
back={this.previousPage}
next={this.nextPage}
/>
)}
{page === 1 && (
<SyncAccount
{...this.props}
activeStep={page}
back={this.previousPage}
next={this.nextPage}
/>
)}
{page === 2 && (
<Extension
{...this.props}
activeStep={page}
back={this.previousPage}
next={this.nextPage}
/>
)}
</CardWrapper>
</Wrapper>
);
}
}
export default connect(
mapStateToProps,
mapDispatchToProps,
)(AccountSteps);
SyncAccount.js
const SyncAccount = ({
user,
emailProvider,
handleProviderChange,
handleChange,
...props
}: {
user: Object,
emailProvider: string,
handleProviderChange: Function,
handleChange: Function
}) => {
console.log('props in Sync', user.email);
return (
<SyncWrapper>
<IconsWrapper>
<CustomSync style={{ fontSize: 30 }} color="action" />
<Mail style={{ fontSize: 50 }} color="secondary" />
</IconsWrapper>
<p>Please enter your email address which you want to sync with us.</p>
<FormWrapper>
<span>Email Provider: </span>
<RadioGroup
aria-label="emailProvider"
name="provider"
style={{ display: 'flex', flexDirection: 'row' }}
value={user.provider}
onChange={handleChange}
>
<FormControlLabel
value="google"
control={<Radio color="primary" />}
label="Google"
/>
<FormControlLabel
value="imap"
control={<Radio color="primary" />}
label="IMAP"
/>
</RadioGroup>
<StyledField
label="Email"
id="email"
name="email"
type="email"
value={user.email}
onChange={handleChange}
placeholder="Email"
component={GTextField}
required
/>
<StyledField
label="Password"
id="password"
name="password"
type="password"
value={user.password}
onChange={handleChange}
placeholder="Password"
component={GPasswordField}
required
/>
<StyledField
label="Job title"
id="jobTitle"
name="job_title"
value={user.job_title}
onChange={handleChange}
placeholder="Job Title"
component={GAutoCompleteField}
required
/>
<Footer
{...props}
userObj={user}
type="sync"
wizardData={{ step_index: 0, wizard_name: 'privacy' }}
disabled={user && (!user.email || !user.password || !user.job_title)}
/>
</FormWrapper>
</SyncWrapper>
);
};
export default enhance(SyncAccount);
enhance.js
const requiredFields = {
email: 'Email',
password: 'Password',
job_title: 'Job Title',
};
const withReduxForm = reduxForm({
form: 'syncAccount',
fields: requiredFields,
destroyOnUnmount: false,
forceUnregisterOnUnmount: true,
validate,
});
const mapStateToProps = state => ({
wizard: state.wizardReducer,
});
const enhance = compose(
connect(
mapStateToProps,
null,
),
withReduxForm,
withState('user', 'updateUser', {
email: '',
password: '',
job_title: '',
provider: 'google',
wizard_name: 'extension',
step_index: 2,
}),
withHandlers({
handleChange: props => (ev) => {
if (ev.target) {
return props.updateUser({
...props.user,
[ev.target.name]: ev.target.value,
});
}
return props.updateUser({
...props.user,
job_title: ev.name,
});
},
}),
);
export default enhance;

Categories