Where to put proptypes validation? - reactjs

Im my react application, Im in the refactoring phase. I want to separate redux part from the components. Im confused about where to put proptypes validation? Should the redux props be validated in the container file, and component props should be validated on the component? Or should the both prop types be handled on the component? Or in container? Here is my code.
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import { registerUser } from '../../../actions';
import { TextFieldGroup } from '../../../components/UI';
class RegisterScreen extends Component {
state = {
name: '',
email: '',
password: '',
password2: '',
errors: {}
};
componentDidMount() {
if (this.props.auth.isAuthenticated) {
this.props.history.push('./dashboard');
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
}
onChange = e => {
this.setState({
[e.target.name]: e.target.value
});
}
onSubmit = e => {
e.preventDefault();
const newUser = {
name: this.state.name,
email: this.state.email,
password: this.state.password,
password2: this.state.password2
}
this.props.registerUser(newUser, this.props.history);
}
render() {
const { errors } = this.state;
return (
<div className='register'>
<div className='container'>
<div className='row'>
<div className='col-md-8 m-auto'>
<h1 className='display-4 text-center'>Sign Up</h1>
<p className='lead text-center'>
Create Your Developer Connector Account
</p>
<form noValidate onSubmit={this.onSubmit}>
<TextFieldGroup
className={errors.email}
placeholder="* Full Name"
name="name"
value={this.state.name}
onChange={this.onChange}
error={errors.name}
/>
<TextFieldGroup
type='email'
className={errors.email}
placeholder="* Email Address"
name="email"
value={this.state.email}
onChange={this.onChange}
error={errors.email}
info='This site uses Gravatar so if you want a profile image, use a Gravatar email'
/>
<TextFieldGroup
type='password'
className={errors.password}
placeholder="* Password"
name="password"
value={this.state.password}
onChange={this.onChange}
error={errors.password}
/>
<TextFieldGroup
type='password'
className={errors.password2}
placeholder="* Confirm Password"
name="password2"
value={this.state.password2}
onChange={this.onChange}
error={errors.password2}
/>
<input type='submit' className='btn btn-info btn-block mt-4' />
</form>
</div>
</div>
</div>
</div>
);
}
}
RegisterScreen.propTypes = {
registerUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
}
const mapStateToProps = state => ({
auth: state.auth,
errors: state.errors
});
const mapDispatchToProps = dispatch => ({
registerUser: bindActionCreators(registerUser, dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(RegisterScreen));

You should define the PropTypes in the component as you have in the example there. When deciding when to use PropTypes for static typing, you should always guard against incoming props that can directly affect the desired result of the component when rendered. By that I mean, if the component requires "auth" or "errors" props, then you should define the PropTypes for the component receiving those properties, whether through HoC in this case redux or any other mode of passing data down.

Related

submit form with react redux

i implement a single form for each sign up/ login with redux i test the apis with postman before trying to submit data with forms so the login form works fine but when i try to submit the register form nothing happened , just i got the console log message in the navigator console ..
here is my code :
form.js
import React, { useEffect } from "react";
import { Link, useNavigate } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import PropTypes from "prop-types";
import { useForm } from "react-hook-form";
import { login } from "../../redux/index";
import loginSvg from "../../images/Login-amico.svg";
import signUpSvg from "../../images/signup.svg";
function Form({ page }) {
const dispatch = useDispatch();
const navigate = useNavigate();
const userLogin = useSelector((state) => state.userLogin);
const { error: loginError, loading: loginLoading, userInfo } = userLogin;
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
if (page !== "signUp") {
dispatch(login(data.email, data.password));
} else {
console.log("smthg else");
}
};
useEffect(() => {
if (userInfo) {
navigate("/app/dashboard", { replace: true });
}
}, [navigate, userInfo]);
return (
<form onSubmit={handleSubmit(onSubmit)} >
{page === "signUp" ? (
<div className="mb-3">
<label>User Name</label>
<input
type="text"
className="form-control"
placeholder="user name"
{...register("userName", {
required: true,
className:
"form-control form-control-lg",
type: "text",
placeholder: "User Name",
})}
/>
</div>
) : undefined}
<div className="mb-3">
<label>Email address</label>
<input
type="email"
className="form-control"
placeholder="Enter email"
{...register("email", {
required: true,
className: "form-control",
type: "email",
placeholder: "Email address",
})}
/>
</div>
<div className="mb-3">
<label>Password</label>
<input
type="password"
className="form-control"
placeholder="Enter password"
{...register("password", {
required: true,
className: "form-control",
type: "password",
placeholder: "Password",
})}
/>
</div>
<div className="d-grid">
<button type="submit" className="btn btn-primary">
Submit
</button>
</div>
<p className="forgot-password text-right">
Already registered sign in?
</p>
</form>
);
}
Form.propTypes = {
page: PropTypes.string.isRequired,
};
export default Form;
how can i solve this issue ?
Unfortunately I can't comment yet but I have a suggestion. I'm not very familiar with the useForm hook but I see in your onSubmit function that if page is signUp you will execute only a console.log message. Maybe you should add some dispatch to this condition as well.

React Labels not displaying in browser

I am new to React and especially formik. I am trying to learn how to create a login form using formik that will display a label called email with the email input. A label called password with the password input and a button called submit.
My problem is that only the two inputs and submit button displays in the browser. The two labels for email and password do not display in the browser. Please advise how I can fix this.
App.js:
import React from 'react';
import './App.css';
import FormikContainer from './components/FormikContainer';
import LoginForm from './components/LoginForm';
function App() {
return (
<div>
<LoginForm />
</div>
);
}
export default App;
LoginForm.js:
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikContainer from './FormikContainer';
import FormikControl from './FormikControl';
function LoginForm() {
const initialValues = {
email: '',
password: ''
};
const validationschema = Yup.object({
email: Yup.string().email('invalid email format').required('Requird'),
password: Yup.string().required('Required')
});
const onSubmit = (values) => {
console.log('Form data', values);
};
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => {
return (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<FormikControl
control="input"
type="password"
label="Password"
name="password"
/>
<button type="submit" disabled={!formik.isValid}>
Submit
</button>
</Form>
);
}}
</Formik>
</div>
);
}
export default LoginForm;
FormikControl.js:
import React from 'react';
function FormikControl(props) {
const { control, ...rest } = props;
switch (control) {
case 'input':
return <input {...rest} />;
case 'textarea':
case 'select':
case 'radio':
case 'checkbox':
case 'date':
default:
return null;
}
return <div></div>;
}
export default FormikControl;
FormikContainer.js
import React from 'react';
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
import FormikControl from './FormikControl';
function FormikContainer() {
const initialValues = {
email: ''
};
const validationschema = Yup.object({
email: Yup.string().required('Required')
});
const onSubmit = (values) => console.log('Form data', values);
return (
<div>
<Formik
initialValues={initialValues}
validationschema={validationschema}
onSubmit={onSubmit}
>
{(formik) => (
<Form>
<FormikControl
control="input"
type="email"
label="Email"
name="email"
/>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
);
}
export default FormikContainer;
I would like to do easily this instead :
function FormikControl({ control, id, label, ...rest }) {
return (
<>
{control === "input" && <label htmlFor={id}>{label}</label>}
<input id={id} {...rest} />
</>
);
}
export default FormikControl;

Cannot read property 'isAuthenticated' of undefined

TypeError: Cannot read property 'isAuthenticated' of undefined
Function.mapStateToProps [as mapToProps]
E:/smnd/client/src/components/auth/Login.js:69
function mapStateToProps (state){
return{
isAuthenticated: state.auth.isAuthenticated
}
}
this is the code of Login.js
import React, {Fragment, useState } from 'react';
import {Link, Redirect} from 'react-router-dom';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import {login} from '../../actions/auth';
// import {isAuthenticated} from '../../reducers/auth';
const Login = ({login, isAuthenticated}) => {
const [formData, setFormData] = useState({
email: '',
password: ''
});
const { email, password} = formData;
const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = async e => {
e.preventDefault();
login(email,password);
};
// redirected if logged in
if (isAuthenticated) {
return <Redirect to='/dashboard' />;
}
return (
<Fragment>
<h1 className="large text-primary">Sign In</h1>
<p className="lead"><i className="fas fa-user"></i> Sign Into Your Account</p>
<form className="form" onSubmit={e => onSubmit(e)}>
<div className="form-group">
<input type="email" placeholder="Email Address" name="email" value={email} onChange={ e => onChange(e)} />
</div>
<div className="form-group">
<input
type="password"
placeholder="Password"
name="password"
value={password}
onChange={ e => onChange(e)}
/>
</div>
<input type="submit" className="btn btn-primary" value="Login" />
</form>
<p className="my-1">
Don't have an account? <Link to="/register">Sign Up</Link>
</p>
</Fragment>
);
};
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
}
// const mapStateToProps = state => ({
// isAuthenticated: state.auth.isAuthenticated
// });
function mapStateToProps (state){
return{
isAuthenticated: state.auth.isAuthenticated
}
}
export default connect(mapStateToProps, {login}) (Login);
Because you don't set the initial state of auth in the redux store. So you need to add optional chaining like this:
isAuthenticated: state.auth?.isAuthenticated
Make sure that in the login reducer of redux store , you have provided the "isAuthenticated" as initialState like the below..
const initialState = {
isAuthenticated: false,
};
function loginReducer(state = initialState, action){
....
}
Also make sure that you have included the loginReducer in the "combineReducers" function , if you are using multiple reducers for your redux store

Redirect component doesn't change url (React)

I am building Signup component which should redirect to the root path if user has passed all information.
However, Redirect component doesn't work for some reason, as url is not changed. On line 12 I am logging in renderRedirect and I can see this message in my console so I am inside renderRedirect method but, like I said, url stays the same localhost:3000/signupm instead of changing to localhost:3000/.
Any idea why?
import React from "react";
import { Redirect } from "react-router-dom";
class Signup extends React.Component {
state = {
password: "",
fullName: "",
email: ""
};
renderRedirect = () => {
console.log("in renderRedirect");
return <Redirect push to="/" />;
};
handleInputChange = e => {
const value = e.target.value;
const name = e.target.name;
this.setState({ [name]: value });
};
handleSignup = () => {
this.props.onSignup(
{
username: this.state.username,
password: this.state.password,
fullName: this.state.fullName,
email: this.state.email
},
() => this.renderRedirect()
);
};
render() {
return (
<div className="login-wrap">
<h2>Sign Up</h2>
<div className="form">
<input
type="text"
placeholder="Full Name"
name="fullName"
value={this.state.fullName}
onChange={this.handleInputChange}
/>
<input
type="email"
placeholder="Email address"
name="email"
value={this.state.email}
onChange={this.handleInputChange}
/>
<input
type="password"
placeholder="Password"
name="password"
value={this.state.password}
onChange={this.handleInputChange}
/>
<button onClick={this.handleSignup}>Sign up</button>
</div>
</div>
);
}
}
export default Signup;
I would omit the 'push' from the redirect component and see if that works. Otherwise, you could make use of the history object and use this.props.history.push('/') instead. If you don't have access to the routeProps, you can wrap your Signup Component with { withRouter } from 'react-router-dom'

redux-form v6: Form submission canceled because the form is not connected

I am getting this error in my console.
"Form submission canceled because the form is not connected"
after trying to migrate my redux-form from v5 to v6 since we migrated our app to a more recent version of React.
I am not sure what is going wrong here so i figured I could use a second or third pair of eyes.
Here is my "Smart Component"
import React, { PropTypes } from 'react';
import { reduxForm } from 'redux-form/immutable';
import { connect } from 'react-redux';
import { logUserIn } from '../../actions/authentication';
import { VALID_EMAIL_REGEX } from '../../config/app_config';
import LoginForm from './LoginForm';
const FORM_ID = 'loginForm';
export class LoginFormContainer extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
loginAction: PropTypes.func.isRequired,
};
performLogin = (params) => {
const { loginAction } = this.props;
const credentials = {
email: params.email,
password: params.password,
};
loginAction(credentials, '/home');
}
render() {
const { handleSubmit, submitting } = this.props;
return (
<LoginForm
handleSubmit={ handleSubmit }
loginFunction={ this.performLogin }
submitting={ submitting }
/>
);
}
}
const validate = values => {
const errors = {};
if (!values.email || values.email === '') {
errors.email = 'Required';
}
else if (!VALID_EMAIL_REGEX.test(values.email)) {
errors.email = 'Invalid email address';
}
if (!values.password || values.password === '') {
errors.password = 'Required';
}
return errors;
};
LoginFormContainer = reduxForm({
form: FORM_ID,
validate,
})(LoginFormContainer);
export default connect(null, {
loginAction: logUserIn,
})(LoginFormContainer);
I am passing down my submission handler function as a prop to my actual form that contains the Field component for inputs. The loginAction will link to the action for redux to send the values to the backend and redirect to home.
import React, { PropTypes } from 'react';
import { Field } from 'redux-form/immutable';
import { getClassName, checkButtonDisabled } from '../../utils/forms';
import { Link } from 'react-router';
const renderInput = (field) => {
return (
<div className={ getClassName(field.meta.touched, field.meta.error) }>
<input
{...field.input}
className="form-control form-control-success"
type={field.type}
/>
{field.meta.touched &&
field.meta.error &&
<span className="error">{field.meta.error}</span>}
</div>
);
};
export default class LoginForm extends React.Component {
static propTypes = {
handleSubmit: PropTypes.func.isRequired,
loginFunction: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
};
render() {
const {
loginFunction,
submitting } = this.props;
return (
<form onSubmit={ loginFunction.bind(this) }>
<fieldset>
<div>
<label className="form-control-label">
Email address
</label>
<Field
name="email"
component={renderInput}
type="text"
placeholder="example#exampledomain.com"
/>
</div>
<div>
<label className="form-control-label">
Password
</label>
<Field
name="password"
component={renderInput}
type="password"
placeholder="your password"
/>
</div>
</fieldset>
<button
type="submit"
className="btn btn-primary"
disabled={ checkButtonDisabled(submitting) }
>
Log In
</button>
<Link to="/forgot-password">Forgot Password?</Link>
</form>
);
}
}
I successfully was able to get the form to work but when I hit login I get the error above and I am redirected to home, but I am not authenticated and I get a 422 error as well. I couldn't tell if my form connecting is the only error or if my action is not getting the right information from the form submission function.
Got any suggestions?
You are redirected home, because your loginFunction() is fired, but the form is not submitted
There are a couple of things that need to be updated. Your <form> tag must have a corresponding id and it should handle submit by passing your function to redux-form inbuilt submit handler. So you modifying LoginForm class as follows should get the form working
<form id="loginForm" onSubmit={ handleSubmit(this.loginFunction.bind(this)) } >
More about internal redux-form method handleSubmit here: http://redux-form.com/6.5.0/docs/api/Form.md/
Using the answer given to me above I just wanted to clarify what I did to solve the issue.
I grabbed the handleSubmit method that comes from the reduxForm and passed it to the LoginForm as a prop from the container that also retrieved it from the props.
I also imported the Form component from redux-form on the LoginForm component and just simply replaced the normal JSX tag with .
here were the final changes I made.
LoginForm.jsx:
//Added Form from redux-form
import { Field, Form } from 'redux-form/immutable';
render() {
const {
handleSubmit,//defined handleSubmit function from props which comes from the reduxForm being exported to the state.
loginFunction,
submitting } = this.props;
return (
//replaced <form></form> with <Form></Form>
<Form id="loginForm" onSubmit={ handleSubmit(loginFunction.bind(this)) }>
//passed my loginFunction into the handleSubmit function
//added an id to <Form> that corresponds to the forms id that was passed in reduxForm({ form: 'loginForm' })
<fieldset>
<div>
<label className="form-control-label">
Email address
</label>
<Field
name="email"
component={renderInput}
type="text"
placeholder="example#exampledomain.com"
/>
</div>
<div>
<label className="form-control-label">
Password
</label>
<Field
name="password"
component={renderInput}
type="password"
placeholder="your password"
/>
</div>
</fieldset>
<button
type="submit"
className="btn btn-primary"
disabled={ checkButtonDisabled(submitting) }
>
Log In
</button>
<Link to="/forgot-password">Forgot Password?</Link>
</Form>
);

Resources