Just created this form as part of a tutorial. However, my console log is giving me undefined whenever I submit the form. What am I doing wrong?
import React, {Component} from 'react';
import { reduxForm } from 'redux-form';
class Signin extends Component {
handleFormSubmit({email, password}) {
console.log(email); // this gives me 'undefined'
}
render() {
const {handleSubmit, fields: {email, password}} = this.props;
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<fieldset className="form-group">
<label>Email:</label>
<input {...email} className="form-control" />
</fieldset>
<fieldset className="form-group">
<label>Password:</label>
<input {...password} className="form-control" />
</fieldset>
<button action="submit" className="btn btn-primary">Sign in</button>
</form>
);
}
}
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(Signin);
This is due to an update to redux-form.
Rather than importing the values of {email, password} from this.props we instead use a Field component imported from redux-form.
import { Field, reduxForm } from 'redux-form';
Then using it in place of the input tag you have:
<fieldset className="form-group">
<label>Email:</label>
<Field
name="email"
className="form-control"
component="input"
type="text"
placeholder="Email"
/>
</fieldset>
The connection of this input to redux-form now comes from the name property instead of extracting it from this.props
Important to note: that the name MUST be the same as the name in the fields: [] array defined in the reduxForm:
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(Signin);
Building on this, if you want to customise the component that the Field uses, you can define your own custom component pretty easily.
Instead of supplying a string to Field's component property, you can define a function:
<div className="form-group">
<label>Last name:</label>
<Field type="text" className="form-control" placeholder="Smith" name="lastName"
component={textField} />
</div>
textField is imported from another file: import {textField} from '../../redux_form_elements';
And textField is as follows:
import React from 'react';
export const textField = (field) => (
<div>
<input className="form-control" {...field.input} type={field.type} placeholder={field.placeholder} />
{field.meta.touched && field.meta.error &&
<label id="basic-error" className="validation-error-label">This field is required.</label>
}
</div>
);
This looks like the Grider Tutorial. I fixed it this way, with a renderInput function which takes the field. Not sure if this is useful for you.
import React, {Component} from 'react';
import { reduxForm } from 'redux-form';
import { Field } from 'redux-form';
const renderInput = field => (
<div>
<input {...field.input} type={field.type} className="form-control" />
{field.meta.touched && field.meta.error}
<span>{field.meta.error}</span>
</div>
);
class Signin extends Component {
handleFormSubmit({email, password}) {
console.log(email);
console.log("Hi");
}
render() {
const {handleSubmit, fields: {email, password}} = this.props;
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<fieldset className="form-group">
<label>Email</label>
<Field
name="email"
component={renderInput}
type="text" />
</fieldset>
<fieldset className="form-group">
<label>Password</label>
<Field
name="password"
component={renderInput}
type="text" />
</fieldset>
<button action="submit" className="btn btn-primary">Sign in</button>
</form>
);
}
}
export default reduxForm({
form: 'signin',
fields: ['email', 'password']
})(Signin);
Related
I created a login form and now I want to convert my input fields to re- usable component. I created separate common input.jsx file. This is input.jsx file's code.
import React from "react";
const Input = ({ name, label, value, onChange }) => {
return (
<div className="form-group">
<label htmlFor={name}>{label}</label>
<input
value={value}
onChange={onChange}
id={name}
name={name}
type="text"
className="form-control"
/>
</div>
);
};
export default Input;
and imported it to my loginForm.jsx. Here is my loginForm.jsx render method
handleChange = ({ currentTarget: input }) => {
const account = { ...this.state.account };
account[input.name] = input.value;
this.setState({ account });
};
render() {
const { account } = this.state;
return (
<div>
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<Input
name="username"
value={account.username}
label="Username"
onChange={this.handleChange}
/>
<Input
name="password"
value={account.password}
label="Password"
onChange={this.handleChange}
/>
<button className="btn btn-primary">Login</button>
</form>
</div>
);
}
But after adding below code to my loginForm.jsx,
<Input
name="username"
value={account.username}
label="Username"
onChange={this.handleChange}
/>
code and deleted previous code ,
<div className="form-group">
<label htmlFor="username">Username</label>
<input
value={account.username}
name="username"
onChange={this.handleChange}
ref={this.username}
id="username"
type="text"
className="form-control"
/>
</div>
suddenly my login page not loading.(Empty page).
My login page's console showing below error.
The above error occurred in the <LoginForm> component:
at LoginForm (http://localhost:3000/main.5d4e82bfe117bc198b43.hot-update.js:27:5)
at Route (http://localhost:3000/static/js/bundle.js:54444:5)
at Switch (http://localhost:3000/static/js/bundle.js:54739:5)
at main
at App
at Router (http://localhost:3000/static/js/bundle.js:54612:5)
at BrowserRouter (http://localhost:3000/static/js/bundle.js:53870:5)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
How can I define input attributes in typescript? I have an AddUser Component and TextInput Component, I want to import the TextInput component inside the AddUser component and then pass props to the TextInput component.
AddUser.tsx
import Button from '../Shared/Form/Button/Button';
import Form from '../Shared/Form/Form';
import TextArea from '../Shared/Form/TextArea/TextArea';
import TextInput from '../Shared/Form/TextInput/TextInput';
const AddUser = () => {
return (
<div>
<h1>Add User</h1>
<Form method="post" className={'user-form'}>
<TextInput label={'Name'} type="text" name="name" required />
<TextInput label={'Email'} type="email" name="email" required />
<TextInput label={'Country'} type="text" name="country" required />
<TextInput label={'Phone'} type="text" name="phone" required />
<TextArea label={'Address'} name="address" cols="30" rows="4" />
<div className="form-button">
<Button type={'submit'} className={'btn-add'}>
Add
</Button>
<Button type={'submit'} className={'btn-close'}>
Cancel
</Button>
</div>
</Form>
</div>
);
};
export default AddUser;
TextInput.tsx
const TextInput = ({ className, label, ...rest }: { className: string; label: string }) => {
return (
<div className={`${className} form-field`}>
<label htmlFor={label}>{label}</label>
<input {...rest} />
</div>
);
};
export default TextInput;
You can extend HTMLProps which comes with react:
import { HTMLProps } from "react";
interface MyCOmponentProps extends HTMLProps<HTMLInputElement> {
{...}
}
This is how we can make a reusable component. maybe I missed something in onChangeAction according to type script. please let me know me in the comment section so that i can help you better
Example codesandbox
const AddUser = () => {
return (
<div>
<h1>Add User</h1>
<form method="post" className={"user-form"}>
<TextInput
label={"Name"}
placeholder="Name"
type="text"
name="name"
required
/>
<TextInput
label={"Email"}
placeholder="Email"
type="email"
name="email"
required
/>
<TextInput
label={"Country"}
placeholder="Country"
type="text"
name="country"
required
/>
<TextInput
label={"Phone"}
placeholder="Phone"
type="text"
name="phone"
required
/>
</form>
</div>
);
};
export default AddUser;
const TextInput = ({
className,
label,
value,
type,
onChangeAction,
placeholder
}) => {
return (
<div className={`${className} form-field`}>
<label htmlFor={label}>{label}</label>
<input
placeholder={placeholder || "Text"}
type={type || "text"}
value={value}
onChange={onChangeAction}
/>
</div>
);
};
I'm using formik in a reactjs project, and I want to use Prompt from react-router to open a notification before a user leaves and loses changes made to their submission.
I'd expected something like this to work:
<Prompt
when={formik.dirty}
message="You have unsaved changes. Are you sure you want to leave?"
/>
My formik block looks like this:
const formik = useFormik({
initialValues: {
<values>
},
enableReinitialize: true,
validate,
onSubmit: values => {
<submit functional stuff>
}
});
And my form is something like this:
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
disabled={isDisabled}
/>
{formik.errors.name ? <div className="text-danger">{formik.errors.name}</div> : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
disabled={isDisabled}
/>
{formik.errors.subject ? <div className="text-danger">{formik.errors.subject}</div> : null}
</div>
</div>
</form>
but it appears that formik.dirty is either not defined or it's not seen as true (despite making changes to the form).
How would I properly use the dirty prop to trigger the Prompt?
I am not sure what kind of setup you have, but I created a PoC with routing which has two tabs (links) for navigation and I am using prompt on tab with formik form component.
import React from "react";
import { Prompt } from "react-router-dom";
import { useFormik } from "formik";
const MyForm = () => {
const formik = useFormik({
initialValues: {
name: "",
subject: ""
},
enableReinitialize: true,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
onChange: (e) => {
console.log(e);
}
});
return (
<div>
<Prompt
when={!!formik.dirty}
message={(location) =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
/>
{formik.errors.name ? (
<div className="text-danger">{formik.errors.name}</div>
) : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
/>
{formik.errors.subject ? (
<div className="text-danger">{formik.errors.subject}</div>
) : null}
</div>
{formik.dirty && <button tye="submit">Save</button>}
</div>
</form>
</div>
);
};
export default MyForm;
take a look the this codesandbox.
Error:Line 33:13: Expected an assignment or function call and instead saw an expression no-unused-expressions
I used the Route and Browser Router to create the link to the page.
I need to redirect to the home page on signing in.
import axios from 'axios'
import {Redirect} from 'react-router-dom'
import React,{Component} from 'react'
const url ="http://localhost:80/phpfile"
class Signup extends Component{
constructor(){
super();
this.state={
username:"",
password:"",
value:false
}
}
sign=e=>{
e.preventDefault();
const user={
"username":this.state.username,
"password":this.state.password
}
axios.post(url,user,{"header":{ "content-type": "application/json",}}
)
.then(result=>{if (result.data===true)
{
this.setState({value:true});
}
})
if(this.state.value){
<Redirect to='/'/>
}
}
render(){
const value= this.state.value;
return(
<div>
<form action="#">
<label> Username</label>
<input name="name" value="Enter the usrname" id ="name"
onChange={e => this.setState({ username: e.target.value })}/>
<label>Password</label>
<input type="password" placeholder="Enter Password" id="pass"
name="pass" onChange={e => this.setState({password: e.target.value })}/>
<botton onClick={e=>this.sign(e)}>Signin</botton>
</form>
</div>
)
}
}
export default Signup;
As you are not using state anywhere, you can add redirect instead of setting state:
...
.then(result=>{if (result.data===true)
{
return <Redirect to='/'/>
}
})
or if state requirement is there, then based on state you can add the Redirect in return statement of component
render(){
const value= this.state.value;
return(
<div>
{!value ?
(<form action="#">
<label> Username</label>
<input name="name" value="Enter the usrname" id ="name"
onChange={e => this.setState({ username: e.target.value })}/>
<label>Password</label>
<input type="password" placeholder="Enter Password" id="pass"
name="pass" onChange={e => this.setState({password: e.target.value })}/>
<botton onClick={e=>this.sign(e)}>Signin</botton>
</form>)
: (<Redirect to='/'/>)
}
</div>
)
}
The redux-form documentation advises me to render my input and submit-validation errors like this.
const renderField = ({ input, placeholder, className, type, meta: { touched, error } }) => (
<div>
<input {...input} className={className} placeholder={placeholder} type={type}/>
{touched && error && <span><font color="red">{error}</font></span>}
</div>
)
Inside render(){return(<form> </form>)} you are then supposed to create your inputs like this (note component={renderField} in the next code line):
<Field type="password" placeholder="password" className="form-control" component={renderField} name="password"/>
I wanted to customize this in order to fit it better into my own work. But I cannot seem to find a way to target touched and error unless I place the component in renderField, I guess I am still missing some vital knowledge. Where are these meta: {touched, error} properties going exactly and if I can access them somewhere?
Below is my entire container file for your reference.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from "redux-form"
import Title from "../components/Title.js"
const renderField = ({ input, placeholder, className, type, meta: { touched, error } }) => (
<div className={"" + touched && error && "input_error_border"}>
<input {...input} className={className} placeholder={placeholder} type={type}/>
{touched && error && <span><font color="red">{error}</font></span>}
</div>
)
class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.props = props;
}
is_registered(){
if(this.props.user.server && this.props.user.insert){
return (<div>
<p>Thank you for registering {this.props.user.first_name}</p>
<p>You will recieve an email with further instructions shortly.</p>
</div>)
}else{
return <div></div>
}
}
render() {
const { handleSubmit } = this.props
console.log(this.props)
return (
<form onSubmit={ handleSubmit } className="box-sizing mx-auto max_vertical_form_400">
<Title innerH="Register New User"/>
<div className="input-group-btn">
{this.is_registered()}
</div>
<div className="form-group">
<Field type="text" placeholder="first name" className="form-control" component={renderField} name="first_name" />
<Field type="text" placeholder="last name" className="form-control" component={renderField} name="last_name" />
</div>
<div className="form-group">
<Field type="text" placeholder="email" className="form-control" component={renderField} name="email"/>
</div>
<div className="form-group">
<Field type="text" placeholder="company" className="form-control" component={renderField} name="company"/>
<Field type="text" placeholder="department" className="form-control" component={renderField} name="department"/>
</div>
<div className="form-group">
<Field type="password" placeholder="password" className="form-control" component={renderField} name="password"/>
<Field type="password" placeholder="repeat password" className="form-control" component={renderField} name="password_repeated"/>
</div>
<div className="input-group-btn">
<button type="submit" className="btn btn-primary">Submit</button>
</div>
{/* <RegisterFormContainer />
<ThemeContainer /> */}
</form>
);
}
}
function validate(values){
const errors= {};
if(!values.password) errors.password = "missing password";
if (!values.email) {
errors.email = 'Required'
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
return errors;
}
RegisterForm = reduxForm({
form: "register_user_form",
validate
})(RegisterForm)
function mapStateToProps({ user }) {
return { user };
}
export default RegisterForm = connect(mapStateToProps, null)(RegisterForm)
You can use redux-form selectors, specifically getFormMeta to know which fields are dirty or touched and getFormSyncErrors to know the fields having errors.
In your code, you need to change to import the selectors
import { getFormMeta, getFormSyncErrors } from 'redux-form';
add it to your mapStateToProps which might look like this:
function mapStateToProps(state) {
return {
user: state.user,
metaForm: getFormMeta('register_user_form')(state),
formSyncErrors: getFormSyncErrors('register_user_form')(state),
};
}
and then you can reference in your component with this.props.metaForm and this.props.formSyncErrors