redux-from validations in multiple tabs - reactjs

I have a requirement where I have to validate multiple forms(modal) which are in different tabs in a single component in react-js. Each tab has add icon when a user clicks on add icon it opens a modal window (which consist of form). I have to validate that form and submit it. Submit is working fine but here i got confused how to validate using redux-form here. Can anyone help which was the best way to validate the multiple forms? Thanks in advance.

we can handle different submit and validations in redux-form here is an example
redux-form Example
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
/*------- connect redux with redux --------*/
import { connect } from 'react-redux';
/*------- action which all data to data base --------*/
import { addMessage } from './actions'
/*------- redux form --------*/
import { Field, reduxForm } from 'redux-form';
class Form extends Component {
//PRISTINE / DIRTY // TOUCHED / ERROR : events in redux-form
/*------- renderInputField --------*/
renderInputField(field){
const className = `form-input ${field.meta.touched && field.meta.error ? 'has-error':''}`;
return (
<div className={className}>
<label>{field.myLabel}</label>
<input type="text" {...field.input}/>
<div className="error">
{field.meta.touched ? field.meta.error:''}
</div>
</div>
)
}
/*------- renderTextareaField --------*/
renderTextareaField(field){
const className = `form-input ${field.meta.touched && field.meta.error ? 'has-error':''}`;
return(
<div className={className}>
<label>{field.myLabel}</label>
<textarea
{...field.input}
></textarea>
<div className="error">
{field.meta.touched ? field.meta.error:''}
</div>
</div>
)
}
/*------- onSubmit() : runs on submit --------*/
onSubmit(values){
this.props.addMessage(values,()=>{
})
}
render(){
return(
<div className="Form">
<div className="top">
<h3>Add a Message</h3>
<Link to="/">Back</Link>
</div>
<form onSubmit={this.props.handleSubmit((event)=>this.onSubmit(event))}>
<Field
myLabel="Enter Title"
name="title"
component={this.renderInputField}
/>
<Field
myLabel="Enter name"
name="from"
component={this.renderInputField}
/>
<Field
myLabel="Enter message"
name="message"
component={this.renderTextareaField}
/>
<button type="submit">Submit</button>
</form>
</div>
)
}
}
/*------- validate() : validates our form --------*/
function validate(values){
const errors = {};
if(!values.title){
errors.title = "The title is empty"
}
if(!values.from){
errors.from = "The from is empty"
}
if(!values.message){
errors.message = "The error is empty"
}
return errors;
}
/*------- it returns messages when action is called and state going to change --------*/
function mapStateToProps(state){
console.log(state)
return {
success: state.data
}
}
/*------- reduxForm : connects redux-form with react form --------*/
export default reduxForm({
validate,
form:'PostMessage'
})(
connect(mapStateToProps,{addMessage})(Form)
)

Related

Set select default value doesn't work

Using Bulma CSS for a little help I made custom component to make me handle different kind of inputs with redux-form.
I would like to setup a default value for a select input.
import React from 'react';
import { Field } from 'redux-form';
const DropDownItem = ({ spec }) => {
const { name, defaultValue, items, iconLeft } = spec;
return (
<div className="field">
<div className="control has-icons-left">
<div className="select is-fullwidth">
<Field name={name} value={defaultValue} component="select">
{/* loop for countries */}
{items.map((country, i) => (
<option value={country} key={i}>
{country}
</option>
))}
</Field>
</div>
<div className="icon is-left">
<i className={iconLeft} />
</div>
</div>
</div>
);
};
export default DropDownItem;
I have the right value in defaultValue but the dropdown doesn't select this value by default.
The value of a <Field /> is controlled by the value in the redux-form store. You should set the initialValues for your redux-form form in the config when you initialize it:
import React from 'react'
import { Field, reduxForm } from 'redux-form'
let MyFormComponent = props => {
const { handleSubmit } = props;
return (
<form onSubmit={handleSubmit}>
<Field name="fieldName" component="input" /> // this field's default value will be 'defaultValue', as set in initialValues
...
</form>
)
}
MyFormComponent = reduxForm({
form: 'myForm',
initialValues: {
'fieldName': 'defaultValue',
...
}
})(MyFormComponent)
export default MyFormComponent;
You can also pass initialValues as a prop to your form component.
source
Seems like defaultValue has been disable in redux-form according to this thread on Github
Instead you can make use the initialValues API of redux-form
You can read more about initialValues on Redux Form Docs or initializefromstate

Set classname for datepicker to use as component in Redux Form field

I am trying to set a className to the DatePicker component in redux form material ui like.
My custom component is like this:
import React from 'react';
import { DatePicker } from 'redux-form-material-ui'
const CustomDatePicker = props => (
<DatePicker
{...props}
/>
)
export default CustomDatePicker;
And I am trying to use it like below:
var datePicker = <CustomDatePicker className="testClass" />;
...
...
<Field name="dateTime" type="text" component={datePicker} format={value => value ? new Date(value) : null} />
but I am getting an error saying Invalid Prop component supplied to Field
Can someone point to me what do I need to fix this?
Thanks!
Redux-form Field passes own props to component that will be rendered, so you can pass className to Field, see example:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { DatePicker } from 'redux-form-material-ui';
import './style.css';
const ExampleForm = props => {
const { handleSubmit, pristine, reset, submitting } = props;
return (
<form onSubmit={handleSubmit}>
<div>
<label>Date</label>
<div>
<Field
className="testClass"
name="date"
component={DatePicker}
/>
</div>
</div>
<div>
<button type="submit" disabled={pristine || submitting}>Submit</button>
</div>
</form>
);
};
export default reduxForm({
form: 'example',
})(ExampleForm);
className='testClass' will be passed to DatePicker component in the example.

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>
);

REDUX-FORM error on handleSubmit

I am getting error while using redux-form
Error msg in console:
bundle.js:32511 Uncaught Error: You must either pass handleSubmit()
an onSubmit function or pass onSubmit as a prop(…)
the above error appeared on page load and button click
Please refer to the below code sample which cause the console error.
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import { createPost } from '../actions/index';
class PostsNew extends Component {
render() {
const { fields: { title, categories, content }, handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.props.createPost)}>
<h3>Create a new Post</h3>
<div className="form-group">
<label>Title</label>
<input type="text" className="form-control" {...title}/>
</div>
<div className="form-group">
<label>Categories</label>
<input type="text" className="form-control" {...categories}/>
</div>
<div className="form-group">
<label>Content</label>
<textarea className="form-control" {...content}/>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
);
}
}
export default reduxForm({
form: 'PostsNewForm',
fields: ['title', 'categories', 'content']
}, null, { createPost })(PostsNew);
This was a step by step follow of StephenGrider redux tutorial
Thanks in advance :)
If PostsNew is Container (if this is directly invoked form Routes) then you have to make handleSubmit function instead of taking from this.props
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import { createPost } from '../actions/index';
class PostsNew extends Component {
handleSubmit(formValues){
console.log(formValues);
//do what ever you want
}
render() {
const { fields: { title, categories, content } } = this.props;
return (
<form onSubmit={this.handleSubmit(this.props.createPost)}>
<h3>Create a new Post</h3>
<div className="form-group">
<label>Title</label>
<input type="text" className="form-control" {...title}/>
</div>
<div className="form-group">
<label>Categories</label>
<input type="text" className="form-control" {...categories}/>
</div>
<div className="form-group">
<label>Content</label>
<textarea className="form-control" {...content}/>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
);
}
}
OR
In case PostsNew is React Component that is used inside a Container then you can pass handleSubmit in props of PostsNew
<PostsNew
handleSubmit={ (values) => {console.log(values)}}
/>
You need to pass onsubmit props from parent component
import React from 'react';
import { connect } from 'react-redux';
import { initialize } from 'redux-form';
import PostsNew from './PostsNew';
class App extends React.Component {
handleSubmit(data) {
console.log('Submission received!', data);
this.props.dispatch(initialize('contact', {})); // clear form
}
render() {
return (
<div id="app">
<h1>App</h1>
<PostsNew onSubmit={this.handleSubmit.bind(this)}/>
</div>
);
}
}
export default connect()(App);
I was running into the same problem until I explicitly imported connect into my file. After that, I was able to call this.props.createPost successfully.
This is my solution:
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form'
import { createPost } from '../actions/index';
import { connect } from 'react-redux';
class PostsNew extends Component {
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.props.createPost)}>
<h3>Create a New Post</h3>
<div className="form-group">
<label>Title</label>
<Field name="title" component="input" type="text" className="form-control" placeholder="Title" />
</div>
<div className="form-group">
<label>Categories</label>
<Field name="categories" component="input" type="text" className="form-control" placeholder="Title" />
</div>
<div className="form-group">
<label>Content</label>
<Field name="content" component="textarea" type="text" className="form-control" placeholder="Title" />
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
);
}
}
export default connect(null, {createPost})(reduxForm({
form: 'PostsNew'
})(PostsNew));
This works perfectly. Check your action creator there should be a typo error in your action creator. Please refer the below action creator. If you followed the steps as he mentioned, this should work perfectly. What the error says is you don't have something called createPost in your props. Further more you can find my working project here in github.
export function createPost(props) {
console.log(props);
const request = axios.post(`${ROOT_URL}/posts${API_KEY}`, props);
return {
type: CREATE_POST,
payload: request
};
}
please install version 5 of redux-form.
npm install --save redux-form#5

Form input using Redux Form not updating

My input field is not updating on key press:
import React, { Component, PropTypes } from 'react';
import { Field, reduxForm } from 'redux-form';
class CitySelector extends Component {
render() {
const { isFetching, pristine, submitting, handleSubmit } = this.props;
return (
<form className="form-horizontal col-xs-offset-1" onSubmit={handleSubmit(this.fetchWeather)}>
<div className="form-group">
<div className="col-md-4 col-xs-4">
<Field name="city"
component={city =>
<input type="text" className="form-control" {...city.input} placeholder="enter a city for a 5 day forecast"/>
}
/>
</div>
<div className="col-md-3 col-xs-3">
<button type="submit" className="btn btn-success">Submit</button>
</div>
</div>
</form>
);
}
}
export default reduxForm({
form: 'cityForm'
})(CitySelector);
Do I need to supply an onChange handler for text inputs?
I was having the same problem and my mistake was very simple.
Here's what I had:
import { combineReducers } from 'redux';
import { reducer as forms } from 'redux-form';
import otherReducer from './otherReducer';
export default combineReducers({ otherReducer, forms });
Notice that I was importing redux-form reducer as forms and passing it as is to my combineReducers (like I did with otherReducer) using ES6 Object property value shorthand.
The problem is that the key used to pass redux-form reducer to our combineReducers MUST be named form, so we have to change it to:
export default combineReducers({ customer, form: forms });
or
import { reducer as form } from 'redux-form';
export default combineReducers({ otherReducer, form });
Hope this helps someone else...
If you are using immutable data structures, instead of:
import { reduxForm } from 'redux-form';
use this:
import { reduxForm } from 'redux-form/immutable';
See here for more info http://redux-form.com/6.7.0/examples/immutable/
I was just having an issue similar to this question, except my form wasn't submitting and my validate function also wasn't firing.
It was due to this:
I changed it from input to something else and it totally broke redux-form SILENTLY
const TextInput = ({
input, <--- DO NOT CHANGE THIS
placeholder,
type,
meta: { touched, error, warning }
}) => {
return (
<div className="row-md">
<input
placeholder={placeholder}
type={type}
className="form-text-input primary-border"
{...input} <--- OR THIS
/>
{touched && ((error && <span>{error}</span>) || (warning && <span>{warning}</span>))}
</div>
)
}
Here's the rest of my input if anyone wants to study it:
<Field
component={TextInput}
type="tel"
name="person_tel"
placeholder="Mobile Phone Number"
value={this.props.person_tel}
onChange={(value) => this.props.updateField({ prop: 'person_tel', value })}
/>
If you supply a custom input component to the Field, then yes you have to call onChange passed within input prop to your component. In fact, you almost got it right by spreading city.input, but there's a catch.
When you define a stateless component (or just any function) inside render() method, it is recreated upon every render. And because this stateless component is passed as a prop (component) to Field, it forces Field to render after each recreation. So, your input is going to lose focus whenever CitySelector component renders, thus, no key presses will be captured.
You should extract your stateless component into a separate definition:
const myInput = ({ input }) => (
<input type="text" className="form-control" {...input} placeholder="enter a city for a 5 day forecast" />
);
class CitySelector extends Component {
render() {
const { isFetching, pristine, submitting, handleSubmit } = this.props;
return (
<form className="form-horizontal col-xs-offset-1" onSubmit={handleSubmit(this.fetchWeather)}>
<div className="form-group">
<div className="col-md-4 col-xs-4">
<Field name="city" component={myInput} />
</div>
<div className="col-md-3 col-xs-3">
<button type="submit" className="btn btn-success">Submit</button>
</div>
</div>
</form>
);
}
}
It also makes your code more legible.
You can find more info on that problem in official docs of Redux Form. Note that you could probably use the default input without creating your own, take a look at simple form example.
I found out/ my problem was my
form: formReducer
was not in rootReducer.
formReducer must be on top. My case:
const rootReducer = combineReducers({
general: generalReducer,
data: combineReducers({
user:userReducer,
todoReducer
}),
form: formReducer
});

Resources