Redux Form: Getting Access to Field Components meta.touched property inside JSX Form - reactjs

I am working on a user sign up page and I want to have the user confirm their password after they enter it in. I am trying to access a specific field component's "meta.touched" property from within the form jsx. I have a renderField method that I can use to access it within that function, but I am not able to get to it outside of that. I am trying to have a third field appear after the password field component has been touched. How would I access that?
for example
<Field
label="Please enter an email for you account"
name="email"
type="text"
component={this.renderField}
/>
<Field
label="Please enter a password for your account"
name="password"
type="password"
component={this.renderField}
/>
{if(password.meta.touched == true) &&
<Field
label="Please confirm your password"
name="passwordConfirm"
type="password"
component={this.renderField}
/>}
here is the full component:
import React, {Component} from 'react';
import { Field, reduxForm } from 'redux-form';
import {connect} from 'react-redux';
import * as actions from '../../actions';
import '../../style/signup.css';
class SignUp extends Component {
renderAlert(){
if(this.props.errorMessage){
return(
<div className="alert alert-danger error-message">
<strong>Oops!</strong>
<h6 className="invalid">{this.props.errorMessage}</h6>
</div>
)
}
}
renderField(field){
return(
<div className="form-group">
<label>{field.label}</label>
<input
className="form-control"
type={field.type}
{...field.input}
/>
<div className="invalid">
{field.meta.touched ? field.meta.error: ''}
</div>
</div>
);
}
handleFormSubmit({email, password}){
this.props.signUpUser({email, password});
}
render(){
const {handleSubmit} = this.props;
return(
<div className="container sign-up-form jumbotron">
<h4>Sign Up</h4>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<Field
label="Please enter an email for you account"
name="email"
type="text"
component={this.renderField}
/>
<Field
label="Please enter a password for your account"
name="password"
type="password"
component={this.renderField}
/>
<Field
label="Please confirm your password"
name="passwordConfirm"
type="password"
component={this.renderField}
/>
{this.renderAlert()}
<button action='submit' className="btn btn-success">
Sign Up <i className="fas fa-user-plus"></i>
</button>
</form>
</div>
)
}
}
function validate(values){
//this function will be called for us
//values is an object that has values user has entered into form
const errors = {};
//if errors has any properties, redux forms assumes
//it is invalid
if(!values.email){
errors.email="Please enter an email to sign up";
}
if(!values.password){
errors.password="Please enter a password to sign up";
}
if(!values.passwordConfirm){
errors.passwordConfirm="Please confirm your password";
}
if(values.password !== values.passwordConfirm){
errors.passwordConfirm="Passwords dont match, please confirm";
}
return errors;
}
function mapStateToProps(state){
return {
errorMessage: state.auth.error
};
}
export default reduxForm({
validate,
form: 'signup'
})(
connect(mapStateToProps, actions)(SignUp)
);

I think the getFormMeta selector will help you here. You can add it to your mapStateToProps:
import { getFormMeta } from 'redux-form';
function mapStateToProps(state) {
return {
errorMessage: state.auth.error,
meta: getFormMeta('signup')(state)
};
}
Docs reference

Related

Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

I'm trying to create a sign up form with an input for a users address. The address input uses the google autocomplete address api.
I'd like to be able to keep it as a Formik field, so I can use Yup validation on it.
The address input component looks like
// Google.jsx
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
/* global google */
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.autocompleteInput = React.createRef();
this.autocomplete = null;
this.handlePlaceChanged = this.handlePlaceChanged.bind(this);
}
componentDidMount() {
this.autocomplete = new google.maps.places.Autocomplete(this.autocompleteInput.current,
{"types": ["address"]});
this.autocomplete.addListener('place_changed', this.handlePlaceChanged);
}
handlePlaceChanged(){
const place = this.autocomplete.getPlace();
console.log(place);
}
render() {
return (
<Field ref={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder="" />
);
}
}
export default SearchBar;
And my Form component looks like:
import React from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import SearchBar from "./Google";
const LoginSchema = Yup.object().shape({
fName: Yup.string().required("Please enter your first name"),
address: Yup.string().required("invalid address"),
});
class Basic extends React.Component {
render() {
return (
<div className="container">
<div className="row">
<div className="col-lg-12">
<Formik
initialValues={{
fName: "",
postal: "",
}}
validationSchema={LoginSchema}
onSubmit={(values) => {
console.log(values);
console.log("form submitted");
}}
>
{({ touched, errors, isSubmitting, values }) =>
!isSubmitting ? (
<div>
<div className="row mb-5">
<div className="col-lg-12 text-center">
<h1 className="mt-5">LoKnow Form</h1>
</div>
</div>
<Form>
<div className="form-group">
<label htmlFor="fName">First Name</label>
<Field
type="text"
name="fName"
className={`mt-2 form-control
${touched.fName && errors.fName ? "is-invalid" : ""}`}
/>
<ErrorMessage
component="div"
name="fName"
className="invalid-feedback"
/>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<Field name="address" component={SearchBar} placeholder="" />
<ErrorMessage
component="div"
name="address"
className="invalid-feedback"
/>
</div>
<button
type="submit"
className="btn btn-primary btn-block mt-4"
>
Submit
</button>
</Form>
</div>
) : (
<div>
<h1 className="p-3 mt-5">Form Submitted</h1>
<div className="alert alert-success mt-3">
Thank for your connecting with us.
</div>
</div>
)
}
</Formik>
</div>
</div>
</div>
);
}
}
export default Basic;
This returns an error of "Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?".
Which is coming from my address input component at:
<Field ref={this.autocompleteInput} id="autocomplete" type="text" name="address" placeholder="" />
Everything else is working, I just need to get past this last hurdle and I'll be good from here.
I will begin looking into the docs, but I'm unfortunately in a rush to get this done so I figured I'd try my luck here!
Any help is greatly appreciated! Thank you!

changing an uncontrolled input of type file in React

i created redux-form to upload form-data with file. but i stumped in how handle the file input, when i tried to select file from form browser console it throw this error
component is changing an uncontrolled input of type file to be controlled. Input elements should not switch from uncontrolled to controlled.......
fileupload.js
import React, { Component } from "react";
import { Field, reduxForm } from "redux-form";
import Card from "#material-ui/core/Card";
class RegisterShop extends Component {
state = {};
renderField(field) {
return (
<div>
<label>{field.label}</label>
<input className="form-control" type={field.type} {...field.input} />
</div>
);
}
onSubmit(values) {
let formData = new FormData();
////
}
render() {
const { handleSubmit } = this.props;
return (
<div>
<Card>
<h1>Register Shop</h1>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<Field
label="Shop Name"
name="shopname"
type="text"
component={this.renderField}
/>
<Field
label="Describe about your shop"
name="description"
type="text"
component={this.renderField}
/>
<Field
label="Email"
name="email"
type="text"
component={this.renderField}
/>
<Field
label="Contact No"
name="mobileno"
type="text"
component={this.renderField}
/>
<Field
label="Location"
name="locatiion"
type="text"
component={this.renderField}
/>
<Field
label="Shop Logo"
name="image"
type="file"
component="----------" //I changed this many ways still get same error
/>
<button type="submit" className="btn btn-primary">
Login
</button>
</form>
<br />
<br />
</Card>
</div>
);
}
}
export default reduxForm({
form: "registershop"
})(RegisterShop);
I think the problem is here.
<input className="form-control" type={field.type} {...field.input} />
First time, field.input is undefined, so fields is blank object , and input field will like this, which is "an uncontrolled input".
<input>undefined</input>
And after type something in field, the field.input will have value,so be controlled component.
And maybe change to this:
<Field
label="Shop Logo"
name="image"
type="file"
component={MyFile}
dataAllowedFileExtensions="jpg png bmp"></Field>
/>
MyFile component:
class MyFile extends Component {
render() {
const { input,dataAllowedFileExtensions } = this.props
const onInputChange = (e) => {
e.preventDefault();
const files = [...e.target.files];
input.onChange(files);
};
return (
<div>
<input type="file"
onChange={onInputChange}
data-allowed-file-extensions={dataAllowedFileExtensions} />
</div>
)
}
}
Because it’s uncontrolled component, you probably want to leave it as:
<input type=“file”>
Then figure out how to process the input.
From:
React docs
In React, an input type="file" is always an uncontrolled component because its value can only be set by a user, and not programmatically.
You should use the File API to interact with the files. The following example shows how to create a ref to the DOM node to access file(s) in a submit handler:
<input type="file" ref={this.fileInput} />

How get values from a select/option field in redux-form?

I am trying to get the values from my second form. It renders some select options and when I hit the delete button it just returns an empty object. How do I get the value from the options. With the normal input fields it would pass values with the name.
For example if I had an input type="text" name="email", when I would submit this it would give my an object like:
{email: "some string"}
Here is the code:
import React , { Component } from 'react';
// import * as actions from '../actions';
import { reduxForm, Field } from 'redux-form';
import {connect} from 'react-redux';
import {postBooks, deleteBook} from '../../actions/booksActions';
class BooksForm extends Component {
renderField(field) {
const { meta: {touched, error} } = field;
const className = `form-group ${touched && error ? 'has-danger' : ''}`;
return (
<div className={className}>
<label className="control-label"><strong>{field.label}:</strong></label>
<input
className="form-control"
type={field.type}
{...field.input}
/>
<div className="text-help">
{ touched ? error : ''}
</div>
</div>
);
}
renderSelectField(field) {
const bookList = _.map(field.options, (book) => {
return (
<option key={book._id}>{book.title}</option>
)
});
return(
<div className="form-group">
<label htmlFor="sel1" className="control-label">{field.label}</label>
<select className="form-control" id="sel1">
{bookList}
</select>
</div>
);
}
onSubmit(values) {
this.props.postBooks(values);
}
onDelete(values) {
console.log(values);
}
render() {
const {handleSubmit} = this.props;
return (
<div>
<div className="well">
<form className="panel" onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div className="panel-body">
<Field
label="Title"
name="title"
type="text"
component={this.renderField}
/>
<Field
label="Description"
name="description"
type="text"
component={this.renderField}
/>
<Field
label="Price"
name="price"
type="text"
component={this.renderField}
/>
<button className="btn btn-primary">Save Book</button>
</div>
</form>
</div>
<form className="Panel" onSubmit={handleSubmit(this.onDelete.bind(this))}>
<div className="panel-body">
<Field
label="Select Book"
name="selectedBook"
options={this.props.books}
component={this.renderSelectField}
/>
<button className="btn btn-danger">Delete</button>
</div>
</form>
</div>
);
}
}
function validate(values) {
const errors = {};
return errors;
}
function mapStateToProps(state) {
return {
books: state.books
}
}
export default reduxForm({
validate,
form: 'bookForm'
})(connect(mapStateToProps, {postBooks, deleteBook})(BooksForm));
In renderSelectField I needed to add {...field.input} into the select to allow redux-form to monitor it.
<select className="form-control" id="sel1" {...field.input}>

how to provide validations for react js form

How to provide validations for react js form.
I have taken form with two fields. if two fields are enabled then only need to enable save button.
import React from 'react'
import { Button, Checkbox, Form } from 'semantic-ui-react'
const FormExampleForm = () => (
<Form>
<Form.Field>
<label>First Name</label>
<input placeholder='First Name' />
</Form.Field>
<Form.Field>
<label>Last Name</label>
<input placeholder='Last Name' />
</Form.Field>
<Form.Field>
<Checkbox label='I agree to the Terms and Conditions' />
</Form.Field>
<Button type='submit'>Submit</Button>
</Form>
)
export default FormExampleForm
In redux forms 6.7 there's a simple property called pristine which can be used to do this. But if the user enters some value in at least one input field the submit button gets activated. To achieve that you may need to do something like this.
render() {
// const { handleSubmit } = this.props;
const {handleSubmit, pristine, reset, submitting} = this.props
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" placeholder="First Name"/>
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={pristine || submitting}>Submit</button>
</form>
);
}
}
But if you need to enable submit button, say when the user inputs values in 2 given fields or so, then this becomes complex. You need to do something like this. Here's my sample usecase. The form has 3 fields firstName, lastName and age. The submit button gets activated only if the user enters values for firstName and lastName input fields here. Please check out the following code.
import React, { Component } from 'react';
import { Field, reduxForm, formValueSelector } from 'redux-form';
import { connect } from 'react-redux';
class UserNew extends Component {
constructor(props) {
super(props);
this.isSubmitEnabled = this.isSubmitEnabled.bind(this);
}
onSubmit(values) {
console.log(values);
}
isSubmitEnabled() {
// Access field values here and validate them
const firstName = this.props.firstNameValue;
const lastName = this.props.lastNameValue;
if(firstName && lastName){
return true;
}
return false;
}
render() {
const { handleSubmit } = this.props;
const isEnabled = this.isSubmitEnabled();
return (
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<div>
<label>First Name</label>
<div>
<Field name="firstName" component="input" type="text" />
</div>
</div>
<div>
<label>Last Name</label>
<div>
<Field name="lastName" component="input" type="text" />
</div>
</div>
<div>
<label>Age</label>
<div>
<Field name="age" component="input" type="text" />
</div>
</div>
<button type="submit" className="btn btn-primary" disabled={!isEnabled}>Submit</button>
</form>
);
}
}
UserNew = reduxForm({
form: 'UserNewForm'
})(
UserNew
);
// Decorate with connect to read form values
const selector = formValueSelector('UserNewForm') // <-- same as form name
UserNew = connect(state => {
const firstNameValue = selector(state, 'firstName')
const lastNameValue = selector(state, 'lastName')
return {
firstNameValue,
lastNameValue,
}
})(UserNew)
export default UserNew;
To access field values you need to use formValueSelector in ReduxForms. For that you need to connect your form to redux store using connect helper.
Hope this helps. Happy coding !
Either you can use Redux-form as its not used in above code, so you can write custom JS validation code. For this you need to follow below
Initialise state in
contructor(){
this.state{
first_name: null,
last_name: null,
}
}
Change state of every input element onChange onChange={()=>this.setState({first_name: event.target.value})}
Now add onClick property to <Button onClick={this.handleSubmit} />
inside handleSubmit() you can get values through local state of component and add validation rules like
if(this.state.first_name =="") {
console.log("Enter first name")
}

want to load initial Values in Redux Form

i am trying to load the initial value in from but couldn't do this, i am using redux-from, i set the profile data in redux store and can access the data through the props(console them) but can't able to show in the form input. i am trying to replicate this redux-from example. but couldn' able to continue it.
below is the code.
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { required, email, maxLength25 } from '../utils/validations';
import { renderField,
renderSelectField,
renderRadioField,
renderTextAreaField,
renderCheckboxField
} from '../utils/textFieldGroup';
import countries from '../utils/countryList';
import { profileUpdate, profile } from '../actions/user';
const validateAndUpdateRecords = (values, dispatch) => {
return dispatch(profileUpdate(values))
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
})
}
class ProfileForm extends React.Component {
componentWillMount(dispatch){
console.log('mount');
this.props.fetchProfile();
}
render(){
const { handleSubmit, submitSucceeded, error, profile } = this.props
console.log('prof',profile);
return (
<div>
<h1>Profile Page</h1>
<form onSubmit={handleSubmit(validateAndUpdateRecords)}>
<div className={typeof error!='undefined'?'show alert alert-danger': 'hidden'}>
<strong>Error!</strong> {error}
</div>
<Field name="fname" type="text" component={renderField} label="First Name"
validate={[ required, maxLength25 ]}
/>
<Field name="lname" type="text" component={renderField} label="Last Name"
validate={[ required, maxLength25 ]}
/>
<Field component={renderRadioField} name="gender" label="Gender" options={[
{ title: 'Male', value: 'male' },
{ title: 'Female', value: 'female' }
]} validate={ required } />
<Field name="country" type="text" data={countries} component={renderSelectField} label="Country"
validate={[ required ]}
/>
<Field name="about_us" type="text" component={renderTextAreaField} label="About Us"
validate={[ required ]}
/>
<Field name="newsletter" type="checkbox" component={renderCheckboxField} label="Newsletter"
validate={[ required ]}
/>
<p>
<button type="submit" disabled={submitSucceeded} className="btn btn-primary btn-lg">Submit</button>
</p>
</form>
</div>
)
}
}
ProfileForm = reduxForm({
form:'profile'
})(ProfileForm)
ProfileForm = connect(
state => ({
initialValues: state.user.profile
})
)(ProfileForm)
export default ProfileForm;
//text field
export const renderField = ({ input, label, type, meta: { touched, error, warning } }) => (
<div className={classnames('form-group', { 'has-error':touched && error })}>
<label className="control-label">{label}</label>
<div>
<input {...input} placeholder={label} type={type} className="form-control"/>
{touched && ((error && <span className="help-block">{error}</span>))}
</div>
</div>
)
Thanks in advance
Finally i figure out the solutions. below is the solutions. We need to add enableReinitialize : true as mentioned below. If our initialValues prop gets updated, form will update too.
ProfileForm = reduxForm({
form:'profile',
enableReinitialize : true
})(ProfileForm)

Resources