want to load initial Values in Redux Form - reactjs

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)

Related

React testing library to work with redux-form issue

Having an issue trying to test redux-forms components. I keep getting an error FormSection must be inside a component decorated with reduxForm() I tried a couple of different ways to implement this but the results were somewhat the same. Here is test code.
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { reducer as formReducer } from 'redux-form';
import { render, screen, cleanup, fireEvent} from '#testing-library/react'
import "#testing-library/jest-dom";
import ContactInformation from '../components/FormSections/ContactInformation'
// Create a reducer that includes the form reducer from redux-form
const rootReducer = combineReducers({
form: formReducer,
});
const store = createStore(rootReducer);
const renderContactInformation = () => {
render(
<Provider store={store}>
<ContactInformation />
</Provider>,
)
}
describe("<ContactInformation />", () => {
test('it should render fields', () => {
renderContactInformation()
const input = screen.getByLabelText("email")
console.log(input)
}
)
})
any thoughts? These seems like it should work but I think I am at a lost or perhaps I just confused my self. Any help will greatly be appreciated.
Here is the code for the ContactInformation.
class ContactInformation extends Component {
zipCodeLocation = (zip) => {
const { dispatch, updateField, formName } = this.props
new Promise((resolve, reject) => {
dispatch({
type: actions.ZIP_CODE_SEARCH,
zipCode: zip,
resolve,
reject,
})
})
.then((response) => {
dispatch(updateField(formName, "contactInformation.state", response.state))
dispatch(updateField(formName, "contactInformation.city", response.city))
})
.catch((error) => console.log({ error }))
}
render() {
const { checkEmailExistence, emailExists, requiredEmail,isExistingInvestor } = this.props
console.log(this.props)
const isDisable = isExistingInvestor === 'yes' ? 'disabled':''
const isRequired = this.props.isOnboarding
const title = 'Contact Information'
return (
<>
<h3 className="formHeadingBorder">{title}</h3>
<FormSection style={{paddingTop: "5%"}} name="contactInformation">
<Form.Item label="First Name" required>
<FirstNameField name="firstName" requiredMark check={isDisable} />
</Form.Item>
<Form.Item label="Middle Name">
<MiddleInitialField check={isDisable} />
</Form.Item>
<Form.Item label="Last Name" required>
<LastNameField name="lastName" check={isDisable} />
</Form.Item>
<Form.Item required={requiredEmail} label="Email Address" validateStatus={emailExists ? "error" : undefined}>
<EmailField name="email" placeholder="Email Address" required={requiredEmail} check={isDisable} />
</Form.Item>
<Form.Item label="Phone Number">
<div className={styles.multiFormItem}>
<div className={styles.phoneNumberField}>
<PhoneNumberField name="phoneNumber" placeholder="Phone Number" check={isDisable} />
</div>
<div className={styles.inlineFormItem}>
<RadioField name="isMobile" label="Mobile">
<Radio value="yes">Yes</Radio>
<Radio value="no">No</Radio>
</RadioField>
</div>
</div>
</Form.Item>
<Form.Item label="Physical Address">
<StreetOneField placeholder="Address Line 1" check={isDisable} isStreetRequired={isRequired} />
</Form.Item>
<Form.Item label=" ">
<StreetTwoField placeholder="Address Line 2" check={isDisable} />
</Form.Item>
<Form.Item label="Postal Code">
<div className={styles.multiFormItem}>
<ZipField isZipRequired={isRequired} placeholder="Postal Code" onBlur={e => this.zipCodeLocation(e.target.value)} check={isDisable} />
<StateField check={isDisable} />
</div>
</Form.Item>
<Form.Item label="City">
<CityField isCityRequired={isRequired} placeholder="City" check={isDisable} />
</Form.Item>
<Form.Item label="Country">
<CountryField placeholder="Country" />
</Form.Item>
</FormSection>
</>
)
}
}
export default connect(null, (dispatch) => {
return{
dispatch,
updateField: (formName, field, data) => dispatch(change( formName, field, data ))
}
},
)(ContactInformation)

How to change a field in a redux-form?

In my react component I am trying to set a field called 'total'. I have imported the change action as a prop into my component:
import React, { Component, Fragment } from 'react'
import { Field, FieldArray, reduxForm, getFormValues, change } from 'redux-form'
import { connect } from 'react-redux'
import { CalcTotal } from './calculationHelper';
const renderField = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<input {...input} type={type} placeholder={label} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
const renderMods = ({ fields, meta: { error, submitFailed } }) => (
<Fragment>
<ul>
<li>
<button type="button" onClick={() => fields.push({})}>
Add Modification
</button>
{submitFailed && error && <span>{error}</span>}
</li>
{fields.map((mod, index) => (
<li key={index}>
<button
type="button"
title="Remove Mod"
onClick={() => fields.remove(index)}
/>
<h4>Mod #{index + 1}</h4>
<Field
name={`${mod}.lastYear`}
type="number"
component={renderField}
label="Last Year"
/>
<Field
name={`${mod}.currentYear`}
type="number"
component={renderField}
label="Current Year"
/>
<Field name={`${mod}.type`} component="select" label="Type">
<option />
<option value="-">Expense</option>
<option value="+">Income</option>
<option value="-">Tax</option>
</Field>
</li>
))}
</ul>
<Field
name="total"
type="number"
component="input"
label="Total modifications"
text="0"
/>
</Fragment>
)
class FieldArraysForm extends Component {
render() {
const { handleSubmit, formValues, change } = this.props
if (formValues) {
console.log('formvalues', formValues);
const test = CalcTotal(2000);
console.log('calc=', test);
debugger
this.props.change('fieldArraysForm', 'total', 5000)
}
return (
<form onSubmit={handleSubmit}>
{/* <button onClick={this.changeStuff}>set total</button> */}
<FieldArray name="mods" component={renderMods} />
<div>
<button type="submit" >
Submit
</button>
</div>
</form>
)
}
}
const mapStateToProps = (state) => ({
formValues: getFormValues('fieldArraysForm')(state),
});
const mapDispatchToProps = {
change
};
// const Example = reduxForm({
// form: 'fieldArraysForm', // a unique identifier for this form
// })(FieldArraysForm)
// const ConnectedForm = connect(
// mapStateToProps,
// mapDispatchToProps,
// )(Example);
// export default ConnectedForm
export default reduxForm({
form: "fieldArraysForm"
})(
connect(
mapStateToProps,
mapDispatchToProps
)(FieldArraysForm)
);
The line where the code fall into an infinite loop:
this.props.change('fieldArraysForm', 'total', 5000)
How /where do I put this statement to make sure the 'total' field is changed and not get into a loop?Which React lifecycle event would suit? I want to fire this whenever there is a form change on any field.
You'll need to move your statement out of the render method and into the componentDidUpdate lifecycle method (you also need an if statement to prevent an infinite loop):
componentDidUpdate(prevProps) {
if (this.props.someValue !== prevProps.someValue) {
this.props.change("formName", "formField", "newFormValue");
}
}
Working example: https://codesandbox.io/s/r5zz36lqnn (selecting the Has Email? radio button populates the email field with test#example.com, unselecting the radio button resets the email field to "")

Validate function not called in redux-form

I am using redux-form 6.3 version and in this validate function getting called. below is my code. Please check what is issue in code.
Also is there any need to do changes related to validation in actions
import React, { Component } from 'react';
import { Field, reduxForm, initialize } from 'redux-form';
import { connect } from 'react-redux';
import * as actions from '../../actions';
const renderField = field => (
<div className="form-group">
<label>{field.input.label}</label>
<input {...field.input} value={field.value} onChange={(e)=> console.log(e.target.value) } />
{field.touched && field.error && <div className="error">{field.error}</div>}
</div>
);
const renderSelect = field => (
<div>
<label>{field.input.label}</label>
<select {...field.input}/>
{field.touched && field.error && <div className="error">{field.error}</div>}
</div>
);
function validate(formProps) {console.log("vvv---", formProps);
const errors = {};
if (!formProps.firstName) {
errors.firstName = 'Please enter a first name';
}
if (!formProps.lastName) {
errors.lastName = 'Please enter a last name';
}
if (!formProps.email) {
errors.email = 'Please enter an email';
}
if (!formProps.phoneNumber) {
errors.phoneNumber = 'Please enter a phone number'
}
if(!formProps.sex) {
errors.sex = 'You must enter your sex!'
}
return errors;
}
class ReduxFormTutorial extends Component {
componentDidMount() {
this.handleInitialize();
}
handleInitialize() {
const initData = {
"firstName": "Puneet",
"lastName": "Bhandari",
"sex": "male",
"email": "test#gmail.com",
"phoneNumber": "23424234"
};
this.props.initialize(initData);
}
handleFormSubmit(formProps) {
//console.log(formProps);
this.state = { firstName : null };
this.props.submitFormAction(formProps);
}
//our other functions will go here
render() {
const { fields : [firstName, lastName], handleSubmit } = this.props;
return (
<div>
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<Field name="firstName" value="" type="text" component={renderField} label="First Name" />
<Field name="lastName" value="" type="text" component={renderField} label="Last Name"/>
<Field name="sex" component={renderSelect} label="Gender">
<option></option>
<option name="male">Male</option>
<option name="female">Female</option>
</Field>
<Field name="email" type="email" component={renderField} label="Email" />
<Field name="phoneNumber" type="tel" component={renderField} label="Phone Number"/>
<button action="submit">Save changes</button>
</form>
</div>
)
}
}
const form = reduxForm({
form: 'ReduxFormTutorial',
fields: [ 'firstName', 'lastName' ],
validate
});
function mapStateToProps(state) {
return {
user: state.user
};
}
export default connect(mapStateToProps, actions)(form(ReduxFormTutorial));

Setting a defaultValue for TimePicker using redux-form and materialUI?

I can't make any version of setting a defaultValue (or defaultTime or even just value) work for initializing a TimePicker in redux-form from state. All other fields populate correctly, but date/time pickers don't respond to anything. Help is muuuuch appreciated!
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import moment from 'moment';
import { Field, reduxForm, FieldArray, formValueSelector } from 'redux-form';
import MenuItem from 'material-ui/MenuItem';
import {
SelectField,
TextField,
DatePicker,
TimePicker
} from 'redux-form-material-ui';
const selector = formValueSelector('myPantry');
let ServiceGroup = ({ service, index, fields, isAppointment, serviceDate }) =>
<div className="service-type-group" key={index}>
<h4>Service</h4>
<button
type="button"
className="remove-button"
onClick={() => fields.remove(index)}>Remove
</button>
<div className="field-group third">
<Field
name={`${service}.day`}
component={SelectField}
floatingLabelText="Day of week"
className="textfield">
<MenuItem value={1} primaryText="Monday" />
<MenuItem value={2} primaryText="Tuesday" />
</Field>
<Field
name={`${service}.from_time`}
component={TimePicker}
value={null}
floatingLabelText="From"
className="textfield"
/>
<Field
name={`${service}.until_time`}
component={TimePicker}
value={null}
floatingLabelText="To"
className="textfield"
/>
</div>
<div className="field-group half">
<Field
name={`${service}.service_type`}
component={SelectField}
floatingLabelText="Service type"
className="textfield">
<MenuItem value={1} primaryText="First-come first-served" />
<MenuItem value={2} primaryText="Appointment" />
</Field>
</div>
{isAppointment &&
<div className="field-group sentence-inline">
<Field
name={`${service}.max_people`}
type="number"
component={TextField}
label="Number of people per timeslot"
className="textfield"
/>
</div>
}
</div>;
ServiceGroup = connect(
(state, props) => ({
isAppointment: selector(state, `${props.service}.service_type`) == 2,
serviceDate: selector(state, `${props.service}.from_time`)
})
)(ServiceGroup);
class MyPantry extends Component {
static propTypes = {
onSubmit: PropTypes.func.isRequired
}
constructor(props, context) {
super(props, context);
}
renderGroups = ({ fields, meta: { touched, error } }) => {
return (
<div>
{fields.map((service, index) =>
<ServiceGroup service={service} fields={fields} index={index} key={index} />
)}
<button
type="button"
className="action-button"
onClick={() => fields.push({})}>Add Service
</button>
{touched && error && <span>{error}</span>}
</div>
);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit}>
<div className="section general">
<Field
name="name"
type="text"
component={TextField}
label="Pantry name"
floatingLabelText="Pantry name"
className="textfield full"
/>
<div className="field-group half">
<Field
name="address_street_1"
type="text"
component={TextField}
label="Address"
floatingLabelText="Address"
className="textfield"
/>
<Field
name="address_street_2"
type="text"
component={TextField}
label="Apartment, suite, etc."
floatingLabelText="Apartment, suite, etc."
className="textfield"
/>
</div>
</div>
<h3 className="section-title">Service Setup</h3>
<div className="section service">
<FieldArray name="service_options" component={this.renderGroups} />
</div>
<div>
<button type="submit" className="action-button">Save Pantry Details</button>
</div>
</form>
);
}
}
MyPantry = reduxForm({
form: 'myPantry'
})(MyPantry);
MyPantry = connect(
state => ({
initialValues: state.pantry.data
})
)(MyPantry);
export default MyPantry;
To set default time of TimePicker:
<TimePicker hintText="Pick your time" defaultTime={new Date(2007, 11, 5, 8, 23, 17)} />
here is a working JSFiddle: https://jsfiddle.net/mu5r94m6/2/
You're using this.props.pantry... inside your renderGroups().
You need to bind that function to be able to use this
add a constructor to the class and use bind on that function
constructor(props) {
super(props);
this.renderGroups = this.renderGroups.bind(this);
}
Can now pass format={null} to Time and Date pickers for this. See thread/closed issue here: https://github.com/erikras/redux-form-material-ui/issues/37
and can view tweets that start here: https://twitter.com/megkadams/status/804363887428665345 and continue here: https://twitter.com/megkadams/status/804369699693793280
<Field
name={`${service}.from_time`}
component={TimePicker}
format={(value, name) => {
if (fromTime) {
return value === '' ? null : new Date(fromTime)
} else {
return null;
}
}}
defaultValue={null}
pedantic
floatingLabelText="From"
className="textfield"
/>

Can't type in text field using redux-form

I have a form in a modal using redux-form. I have several text fields, but you can not type in them. My suspicion is that the text field doesn't get the onChange event from the redux-form but I couldn't find any clue what am I doing good.
My code is:
import React from 'react'
import { Button, Modal, Form, Message } from 'semantic-ui-react'
import { Field, reduxForm } from 'redux-form'
const renderField = ({ input, label, type, meta: { touched, error, warning } }) => {
console.log(input)
return (
<Form.Field>
<label>{label}</label>
<input {...input} placeholder={label} type={type} />
{touched && (error && <Message error>{error}</Message>)}
</Form.Field>
)}
let AddNewModal = (props) => {
const { handleSubmit, pristine, submitting, closeNewSite, isAddNewOpen, submit } = props
return (
<Modal dimmer='blurring' open={isAddNewOpen} onClose={closeNewSite}>
<Modal.Header>Add a new site</Modal.Header>
<Modal.Content>
<Form onSubmit={handleSubmit}>
<Form.Group widths='equal'>
<Field name='domain' type='text' component={renderField} label='Domain' />
<Field name='sitemap' type='text' component={renderField} label='Sitemap URL' />
</Form.Group>
/**
* Other fields
* /
<Button type='submit' disabled={pristine || submitting}>Save</Button>
</Form>
</Modal.Content>
<Modal.Actions>
<Button color='black' onClick={closeNewSite} content='Close' />
<Button positive icon='save' labelPosition='right' onClick={submit} content='Save' disabled={pristine || submitting} />
</Modal.Actions>
</Modal>
)
}
export default reduxForm({
form: 'newsite'
})(AddNewModal)
I added the reducer and still got the same issue. At last, I found it must add the attr 'form'.
const reducers = {
routing,
form: formReducer
};
I found the problem. I forgot to inject the redux-form's reducer.
I actually had a similar issue. I will post the code that I am working on for form validation with V6 of redux-form. It works right now but the things you want to look at are componentDidMount, handleInitialize, and handleFormSubmit. Where I figured this out link.
/**
* Created by marcusjwhelan on 10/22/16.
*/
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { reduxForm, Field } from 'redux-form'; // V6 !!!!!!!!
import { createPost } from '../actions/index';
const renderInput = ({ input, label, type, meta: {touched, invalid, error }}) => (
<div className={`form-group ${touched && invalid ? 'has-danger' : ''}`}>
<label>{label}</label>
<input className="form-control" {...input} type={type}/>
<div className="text-help" style={{color: 'red'}}>
{ touched ? error : '' }
</div>
</div>
);
const renderTextarea = ({ input, label, type, meta: {touched, invalid, error }}) => (
<div className={`form-group ${touched && invalid ? 'has-danger' : ''}`}>
<label>{label}</label>
<textarea className="form-control" {...input}/>
<div className="text-help" style={{color: 'red'}}>
{ touched ? error : '' }
</div>
</div>
);
class PostsNew extends Component{
componentDidMount(){
this.handleInitialize();
}
handleInitialize(){
const initData = {
"title": '',
"categories": '',
"content": ''
};
this.props.initialize(initData);
}
handleFormSubmit(formProps){
this.props.createPost(formProps)
}
render(){
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<h3>Create A New Post</h3>
<Field
label="Title"
name="title"
type="text"
component={renderInput} />
<Field
label="Categories"
name="categories"
type="text"
component={renderInput}
/>
<Field
label="Content"
name="content"
component={renderTextarea}
/>
<button type="submit" className="btn btn-primary" >Submit</button>
</form>
);
}
}
function validate(formProps){
const errors = {};
if(!formProps.title){
errors.title = 'Enter a username';
}
if(!formProps.categories){
errors.categories = 'Enter categories';
}
if(!formProps.content){
errors.content = 'Enter content';
}
return errors;
}
const form = reduxForm({
form: 'PostsNewForm',
validate
});
export default connect(null, { createPost })(form(PostsNew));
You need to connect form reducer to your combine reducers
form: formReducer
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import authReducer from './authReducer';
import productsReducer from './productsReducer';
export default combineReducers({
auth: authReducer,
form: formReducer,
products: productsReducer,
});

Resources