I'm a react js beginner. I got stuck in this problem for many days. I don't know how to redirect to path /users when the login is successful (no validation errors). If anything not clear, please tell me. It's my first time to ask question on stackoverflow. Please help :(
//app.js
import React from "react"
import ValidationLog from "./ValidationLog"
import PostList from "./PostList"
import {BrowserRouter as Router,Route,Switch,Redirect} from "react-router-dom"
function App() {
return (
<Router>
<div className="App">
<Switch>
<Route exact path="/" component={ValidationLog}></Route>
<Route path="/users" component={PostList}></Route>
</Switch>
</div>
</Router>
)
}
export default App
//ValidationLog.js
import React from "react"
import {Formik} from "formik"
import * as Yup from "yup"
import {BrowserRouter as Router,Route,Switch,Redirect} from "react-router-dom"
const ValidationLog = () => (
<Formik
initialValues = {{email: "", password: ""}}
onSubmit = {(values,{ setSubmitting}) => {
setTimeout(() => {
<Redirect to="/users" />
}, 500);
}}
validationSchema = {Yup.object().shape({
email: Yup.string()
.email()
.required("Required"),
password: Yup.string()
.required("No password provided")
.min(8,"Password has to be at least 8 characters.")
})
}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props
return (
<div>
<form autoComplete = "off" onSubmit = {handleSubmit}>
<h2 style={{fontSize: 22}}>Login to view our user pool</h2>
<label htmlFor="email">Email:</label>
<input type="text" value={values.email} name="email" onChange={handleChange} onBlur={handleBlur} placeholder="Enter your email" className={errors.email && touched.email && "error"}/>
{errors.email && touched.email && (<div className="input-feedback">{errors.email}</div>)}
<label htmlFor="email">Password:</label>
<input type="password" value={values.password} name="password" onChange={handleChange} onBlur={handleBlur} placeholder="Enter your password" className={errors.password && touched.password && "error"}/>
{errors.password && touched.password && (<div className="input-feedback">{errors.password}</div>)}
<button type="submit" disabled={isSubmitting}>Login</button>
</form>
</div>
)
}}
</Formik>
)
export default ValidationLog
The following is what I tried. I tried to add a <Redirect to /users/> in ValidationLog.js
onSubmit = {(values,{ setSubmitting}) => {
setTimeout(() => {
<Redirect to="/users" />
}, 500);
}}
I also tried to set a variable islogin in ValidationLog.js and import in app.js when there is no validation errors but islogin is always false in app.js.
//ValidationLog.js
export let islogin = false
onSubmit = {(values,{ setSubmitting}) => {
setTimeout(() => {
islogin = true
console.log(`Logged in ${islogin}`)
}, 500);
}}
//app.js
import ValidationLog,{islogin} from "./ValidationLog"
<Route exact path="/">
{islogin ? <Redirect to="/users" /> : null}
</Route>
<Redirect ... /> is a JSX-Construct to create a component at this position. By itself this does not do anything in imperative code-paths like your onSubmit handler.
What you are looking for is an imperative function that you can call outside a React-Tree. Like from the useHistory hook from react-router.
import { useHistory } from "react-router-dom";
...
const history = useHistory();
...
onSubmit={() => history.push("/home")}
More specifically for your example:
const ValidationLog = () => {
const history = useHistory();
return (
<Formik
initialValues = {{email: "", password: ""}}
onSubmit = {(values,{ setSubmitting}) => {
setTimeout(() => {
history.push(...)
}, 500);
}}
...
You can do it using withRouter
Import withRouter from react-router-dom and enclose your component with withRouter in the export.
You can then navigate using this.props.history.push("/newurl")
import React from "react"
import {Formik} from "formik"
import * as Yup from "yup"
// import withRouter
import {BrowserRouter as Router,Route,Switch,Redirect,withRouter} from "react-router-dom"
const ValidationLog = (props) => (
<Formik
initialValues = {{email: "", password: ""}}
onSubmit = {(values,{ setSubmitting}) => {
setTimeout(() => {
// this redirects to /users, make sure you get the import withRouter (check the last export line)
props.history.push("/users");
}, 500);
}}
validationSchema = {Yup.object().shape({
email: Yup.string()
.email()
.required("Required"),
password: Yup.string()
.required("No password provided")
.min(8,"Password has to be at least 8 characters.")
})
}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange,
handleBlur,
handleSubmit
} = props
return (
<div>
<form autoComplete = "off" onSubmit = {handleSubmit}>
<h2 style={{fontSize: 22}}>Login to view our user pool</h2>
<label htmlFor="email">Email:</label>
<input type="text" value={values.email} name="email" onChange={handleChange} onBlur={handleBlur} placeholder="Enter your email" className={errors.email && touched.email && "error"}/>
{errors.email && touched.email && (<div className="input-feedback">{errors.email}</div>)}
<label htmlFor="email">Password:</label>
<input type="password" value={values.password} name="password" onChange={handleChange} onBlur={handleBlur} placeholder="Enter your password" className={errors.password && touched.password && "error"}/>
{errors.password && touched.password && (<div className="input-feedback">{errors.password}</div>)}
<button type="submit" disabled={isSubmitting}>Login</button>
</form>
</div>
)
}}
</Formik>
)
// changes
export default withRouter(ValidationLog)
<Redirect> component approach here:
Use a state variable in your component
const [submitted, setSubmitted] = useState(false);
On submitting, if conditions for redirect are met set this state variable to true
setSubmitted(true);
In your component JSX, place the <Redirect to="/users" /> conditionally depending on that state variable
return <Fragment>
{ submitted
? <Redirect to="/users" />
: <MyComponent ...> }
</Fragment>
Note
If you prefer to avoid this ternary operator syntax for whaterver reason, you could solve this with variables in the component logic before the return. Might be cleaner depending on the complexity of the component.
Related
This is the Login File, I think this is where my issue is coming from. I am a beginner in React and Redux, so this is where I am doing it.
import React, { useState } from 'react'
import Field from './Field'
import useStyles from './styles'
import styles from './Login.module.css'
import { GoogleLogin } from 'react-google-login'
import {useDispatch} from 'react-redux'
import { useHistory, Link } from 'react-router-dom'
import { signup, signin } from '../../actions/auth'
import { Avatar, Button, Paper, Grid, Typography, Container } from '#material-ui/core'
import LockOutlinedIcon from '#material-ui/icons/LockOutlined'
import { createProfile } from '../../actions/profile'
import Google from './Google'
import { useSnackbar } from 'react-simple-snackbar'
import CircularProgress from '#material-ui/core/CircularProgress';
const initialState ={ firstName: '', lastName: '', email: '', password: '', confirmPassword: '', profilePicture: '', bio: ''}
const Login = () => {
const classes = useStyles();
const [formData, setFormData] = useState(initialState)
const [isSignup, setIsSignup] = useState(false)
const dispatch = useDispatch()
const history = useHistory()
const [showPassword, setShowPassword] = useState(false);
// eslint-disable-next-line
const [openSnackbar, closeSnackbar] = useSnackbar()
const user = JSON.parse(localStorage.getItem('profile'))
const [loading, setLoading] = useState(false)
const handleShowPassword = () => setShowPassword(!showPassword);
const handleChange =(e)=> {
setFormData( {...formData, [e.target.name] : e.target.value} )
}
const handleSubmit =(e) => {
e.preventDefault()
if(isSignup) {
dispatch(signup(formData, openSnackbar, setLoading))
} else {
dispatch(signin(formData, openSnackbar, setLoading))
}
setLoading(true)
}
const switchMode =() => {
setIsSignup((prevState) => !prevState)
}
const googleSuccess = async (res) => {
console.log(res)
const result = res?.profileObj
const token = res?.tokenId
dispatch(createProfile({name: result?.name, email: result?.email, userId: result?.googleId, phoneNumber: '', businessName: '', contactAddress: '', logo: result?.imageUrl, website: ''}))
try {
dispatch({ type: "AUTH", data: {result, token}})
window.location.href='/dashboard'
} catch (error) {
console.log(error)
}
}
const googleError =(error) => {
console.log(error)
console.log("Google Sign In was unsuccessful. Try again later")
}
if(user) {
history.push('/dashboard')
}
return (
<Container component="main" maxWidth="xs">
<Paper className={classes.paper} elevation={2}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">{ isSignup ? 'Sign up' : 'Sign in' }</Typography>
<form className={classes.form} onSubmit={handleSubmit}>
<Grid container spacing={2}>
{ isSignup && (
<>
<Field name="firstName" label="First Name" handleChange={handleChange} autoFocus half />
<Field name="lastName" label="Last Name" handleChange={handleChange} half />
</>
)}
<Field name="email" label="Email Address" handleChange={handleChange} type="email" />
<Field name="password" label="Password" handleChange={handleChange} type={showPassword ? 'text' : 'password'} handleShowPassword={handleShowPassword} />
{ isSignup && <Field name="confirmPassword" label="Repeat Password" handleChange={handleChange} type="password" /> }
</Grid>
<div className={styles.buttons}>
<div>
{/* <button className={styles.submitBtn}> { isSignup ? 'Sign Up' : 'Sign In' }</button> */}
{/* <ProgressButton>{ isSignup ? 'Sign Up' : 'Sign In' }</ProgressButton> */}
{loading ? <CircularProgress />
:
<button className={styles.loginBtn} >{ isSignup ? 'Sign Up' : 'Sign In' }</button>
}
</div>
<div>
<GoogleLogin
clientId = {process.env.REACT_APP_GOOGLE_CLIENT_ID}
render={(renderProps) => (
<button className={styles.googleBtn} onClick={renderProps.onClick} disabled={renderProps.disabled} ><Google /> Google</button>
)}
onSuccess={googleSuccess}
onFailure={googleError}
cookiePolicy="single_host_origin"
/>
</div>
</div>
<Grid container justifyContent="flex-end">
<Grid item>
<Button onClick={switchMode}>
{ isSignup ? 'Already have an account? Sign in' : "Don't have an account? Sign Up" }
</Button>
</Grid>
</Grid>
<Link to="forgot"><p style={{textAlign: 'center', color: '#1d7dd6', marginTop: '20px'}}>Forgotten Password?</p></Link>
</form>
</Paper>
</Container>
)
}
export default Login
This is the Testing File
import React from 'react';
import {render,screen, cleanup} from '#testing-library/react'
import userEvent from '#testing-library/user-event';
// file imports
import user from '../../../../server/controllers/user'
import Login from '../Login/Login'
test("should render Login Component",()=>{
render(<Login />);
const linkElement = screen.getByText(/Google/i);
expect(linkElement).toBeInTheDocument();
})
I am getting this error
● should render Login Component
could not find react-redux context value; please ensure the component is wrapped in a <Provider>
23 | const [formData, setFormData] = useState(initialState)
24 | const [isSignup, setIsSignup] = useState(false)
> 25 | const dispatch = useDispatch()
| ^
26 | const history = useHistory()
27 | const [showPassword, setShowPassword] = useState(false);
28 | // eslint-disable-next-line
Should I wrap my code in a provider, if yes, where should I?
PS: I was thinking of wrapping it with a provider on the Login return function.
i'm quite new with react and i'm building a form with react-hook and useState to manage my datas after the submit.
I'm not able to use textfield as they are blocked. I think that i make some errors into value/onChange parameters but i don't know what type of error.
import React, { useState } from "react";
import {
TextField,
MenuItem,
Typography,
Checkbox,
Divider,
Button,
} from "#mui/material";
import { MdError } from "react-icons/md";
import { BsArrowRight } from "react-icons/bs";
import "../style/contactform.scss";
import { useForm } from "react-hook-form";
const initialState = {
name: "",
email: "",
};
const ContactForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const [state, setState] = useState(initialState);
const { name, email } = state;
const handleInputChange = (e) => {
const { name, value } = e.target;
setState({ ...state, [name]: value });
};
const onSubmit = (e) => {
e.preventDefault();
console.log("form submit");
setState(initialState);
};
return (
<form className="contact-form" onSubmit={handleSubmit(onSubmit)}>
<Typography variant="h4" className="form-title">
Be the first.
</Typography>
<div className="form-component">
<TextField
id="standard-basic"
label="Nome*"
variant="standard"
name="nome"
value={name}
onChange={handleInputChange}
{...register("nome", {
required: true,
})}
/>
{errors?.nome?.type === "required" && (
<MdError className="form-validation-icon" />
)}
</div>
<Divider className="form-hr" />
<div className="form-component">
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
value={email}
onChange={handleInputChange}
{...register("email", {
required: true,
pattern: {
value:
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
},
})}
/>
{errors?.email?.type === "required" && (
<MdError className="form-validation-icon" />
)}
{errors?.email?.type === "pattern" && (
<Typography variant="p" className="form-validation-email">
Inserisci un indirizzo email valido.
</Typography>
)}
</div>
<Divider className="form-hr" />
<Button className="form-submit" type="submit" variant="contained">
<BsArrowRight />
</Button>
</form>
);
};
export default ContactForm;
Textfields are completely block but initial state is actually working, do i miss something?
Can you help me?
To assign initial values using the useForm hook, you pass it under the defaultValues parameter passed to the hook like so:
const {
register,
handleSubmit,
reset
formState: { errors },
} = useForm({
defaultValues: initialState
});
Then just pass the ...register name and email to the inputs. There is no need to assign values to them again:
<TextField
id="standard-basic"
label="Name*"
variant="standard"
name="name"
{...register("name", {
required: true,
})}
/>
// for the email..
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
{...register("email", {
required: true,
pattern: {
value: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,},
})}
/>
If you'll notice, the values are off the text fields already and there's also no need for the handleInputChange function. The useForm hook takes care of that.
Edit:
In addition to the onSubmit function, the handleSubmit from useForm passes a data object into the function like this:
const onSubmit = (data) => {
console.log("form submitted", data);
reset(); // this can be destructured of the `useForm` hook.
};
For more info check their documentation
I have been trying to implement a login page functionality in react using Ant Desing forms.
I keep getting the following error
I tried checking all the semicolons to see if this is due to a missing semicolon but was unable to find the root cause.
TypeError: e.preventDefault is not a function
at onSubmit (LoginForm.js:27)
at onFinish (LoginForm.js:44)
at onFinish (Form.js:73)
at useForm.js:811
Here is my code for the login page.
import React, {useState} from 'react';
import {Link, Redirect} from 'react-router-dom';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import styles from './NormalLoginForm.css';
import { Form, Input, Button, Checkbox, Layout, Card } from 'antd';
import { UserOutlined, LockOutlined } from '#ant-design/icons';
import {login} from '../auth/actions/auth';
const NormalLoginForm = ({ login, isAuthenticated }) => {
const [formData, setFormData] = useState({
username: '',
password:''
});
const { username, password} = formData;
const onChange = e => setFormData({...formData, [e.target.name]: e.target.value});
const onFinish = (values) => {
console.log('Received values of form onFinish: ', values);
// login(username,password)
};
const onSubmit = e => {
e.preventDefault();
console.log('Received values of form(onSumbit):' );
login(username,password);
};
if (isAuthenticated)
return <Redirect to='/' />;
return (
<Form
name="normal_login"
className="login-form"
initialValues={{
remember: true,
}}
onFinish={e=> onSubmit(e)}
style={{ maxWidth: 300, align: 'center'}}
>
<Form.Item
name="username"
rules={[
{
required: true,
message: 'Please input your Username!',
},
]}
>
<Input prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Username"
onChange={e=> onChange(e)}
/>
</Form.Item>
<Form.Item
name="password"
rules={[
{
required: true,
message: 'Please input your Password!',
},
]}
>
<Input
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
onChange={e => onChange(e)}
/>
</Form.Item>
<Form.Item>
<Form.Item name="remember" valuePropName="checked" noStyle>
<Checkbox>Remember me</Checkbox>
</Form.Item>
<a className="login-form-forgot" href="">
Forgot password
</a>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
{/*Or register now!*/}
</Form.Item>
</Form>
);
};
NormalLoginForm.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
};
const mapStateToProps = state => ({
isAuthenticated: state.auth.isAuthenticated
});
connect(mapStateToProps, { login })(NormalLoginForm);
function LoginForm () {
return (
<NormalLoginForm/>
)
}
export default LoginForm;
Where am I making the mistake? this is my first project in React, been stuck here for weeks. Kindly help.
If you want to call preventDefault when the form gets submitted, use an onSubmit handler instead. onFinish's parameter is not the event, but the values.
onSubmit={e => e.preventDefault()}
onFinish={onFinish}
const onFinish = (values) => { // you can remove this parameter if you don't use it
login(username, password);
};
After submitting this form I want to be able to redirect the user to the next page (posts page).
However I tried by creating a <button onClick={() => <Redirect to="/posts" />} but seems not to work. Days ago it worked just fine for me but now I don't know what I'm missing...
This is the current file where I'm submitting a form and want the user to be redirected after submitting it
Auth.js
import React, { useState } from 'react'
import { actions } from './sagaSlice';
import { connect } from 'react-redux';
import { BrowserRouter as Redirect } from "react-router-dom";
const Auth = (props) => {
const [formData, setFormData] = useState({
username: '',
email: '',
password: ''
});
const handleFormData = e =>
setFormData({
...formData,
[e.target.name]: e.target.value
});
const { username, email, password } = formData;
const handleSubmit = e => {
e.preventDefault();
props.login({
username,
email,
password
});
}
return (
<div>
<form
onSubmit={handleSubmit}
>
<input
type="text"
name="username"
placeholder="Username"
onChange={handleFormData}
value={username}
/>
<input
type="email"
name="email"
placeholder="Email"
onChange={handleFormData}
value={email}
/>
<input
type="password"
name="password"
placeholder="Password"
onChange={handleFormData} value={password} />
<button
type="submit"
onClick={() => <Redirect to="/posts" />}
>
Login
</button>
</form>
<button onClick={() => <Redirect to="/posts" />}>Go to posts</button>
</div>
);
}
const mapToProps = (state, ownProps) => {
return ({
user: state.user
})
}
export default connect(mapToProps, actions)(Auth);
And my App.js file
App.js
import React from 'react';
import { connect } from 'react-redux';
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Auth from '../Auth/Auth';
import PostsView from '../PostsView/PostsView';
import PostViewSingle from '../PostViewSingle/PostViewSingle';
const App = (props) => {
return (
<div className="container">
<Router>
<Switch>
<Route path="/login" component={Auth} />
<Route path="/posts" component={PostsView} />
<Route path="/post/:id" component={PostViewSingle} />
</Switch>
</Router>
</div>
);
}
const mapToProps = (state) => {
return ({
isAuthenticated: state.user.isAuthenticated
})
}
export default connect(mapToProps, null)(App);
It's hard to say for sure what's wrong without a minimal example of some sort. That being said there's a few things that might help you out:
You're (probably) incorrectly importing the Redirect component (you import BrowserRouter as Redirect). That should (again, probably) be changed to import { Redirect } from "react-router-dom";, assuming you still need the Redirect component.
You want to redirect a user after submitting the form. Therefore, your redirect logic should be in your handleSubmit callback. From there you can handle the redirect.
Here's an updated version of your Auth component:
import { Link, Redirect } from "react-router-dom";
const Auth = (props) => {
const [formData, setFormData] = useState({
username: '',
email: '',
password: ''
});
const handleFormData = e =>
setFormData({
...formData,
[e.target.name]: e.target.value
});
const { username, email, password } = formData;
const handleSubmit = e => {
e.preventDefault();
props.login({
username,
email,
password
});
// Perhaps also check if login was successful?
history.push("/posts");
}
return (
<div>
<form
onSubmit={handleSubmit}
>
<input
type="text"
name="username"
placeholder="Username"
onChange={handleFormData}
value={username}
/>
<input
type="email"
name="email"
placeholder="Email"
onChange={handleFormData}
value={email}
/>
<input
type="password"
name="password"
placeholder="Password"
onChange={handleFormData} value={password} />
<button type="submit">
Login
</button>
</form>
<Link to="/posts">Go to posts</Link>
</div>
);
}
I'm following a tutorial and I am getting confused with my arrow version of it and their function version.
I have a LoaderButton.js and I can write the component as a normal functional component or an arrow component:
Functional Component:
export default function LoaderButton({
isLoading,
className = "",
disabled = false,
...props
}) {
return (
<Button
className={`LoaderButton ${className}`}
disabled={disabled || isLoading}
{...props}
>
{isLoading && <Glyphicon glyph="refresh" className="spinning" />}
{props.children}
</Button>
);
}
Arrow Component:
const LoaderButton = (
isLoading,
className = "",
disabled = false,
...props ) => (
<Button
className={`LoaderButton ${className}`}
disabled={disabled || isLoading}
{...props}
>
{isLoading && <Glyphicon glyph="refresh" className="spinning" />}
{props.children}
</Button>
)
export default LoaderButton
And the LoaderButton is imported and used here in my Login.js:
export default function Login() {
const history = useHistory();
const { userHasAuthenticated } = useAppContext();
const [isLoading, setIsLoading] = useState(false);
const [fields, handleFieldChange] = useFormFields({
email: "",
password: ""
});
function validateForm() {
return fields.email.length > 0 && fields.password.length > 0;
}
async function handleSubmit(event) {
event.preventDefault();
setIsLoading(true);
try {
await Auth.signIn(fields.email, fields.password);
userHasAuthenticated(true);
history.push("/");
} catch (e) {
onError(e);
setIsLoading(false);
}
}
return (
<div className="Login">
<form onSubmit={handleSubmit}>
<FormGroup controlId="email" bsSize="large">
<ControlLabel>Email</ControlLabel>
<FormControl
autoFocus
type="email"
value={fields.email}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="password" bsSize="large">
<ControlLabel>Password</ControlLabel>
<FormControl
type="password"
value={fields.password}
onChange={handleFieldChange}
/>
</FormGroup>
<LoaderButton
block
type="submit"
bsSize="large"
isLoading={isLoading}
disabled={!validateForm()}
>
Login
</LoaderButton>
</form>
</div>
);
}
The standard functional component works as expected.
But the arrow function component seems to have isLoading stuck to true
AND gets this error:
Warning: Failed prop type: Invalid prop `disabled` of type `object` supplied to `Button`, expected `boolean`.
I thought arrow function components were supposed to be a simpler way to write function components.
I keep thinking it has to do with binding and therefore it's somehow binding the props I have different but I can't find any information on the differences of their bindings. I thought if my Login.js is binding according to the way it's written then I should be fine?
I honestly would prefer to write using an arrow function syntax.
They aren't quite equivalent. You didn't destructure props correctly. Wrap all the props with {} so your functional component is taking a single props argument.
const LoaderButton = ({
isLoading,
className = "",
disabled = false,
...props
}) => (
<Button
className={`LoaderButton ${className}`}
disabled={disabled || isLoading}
{...props}
>
{isLoading && <Glyphicon glyph="refresh" className="spinning" />}
{props.children}
</Button>
);
export default LoaderButton