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)
Related
Currently, when I select city dropdown, it triggers a re-validation of already filled input fields.Image description here
I'm using the County-State-City library to look up the states and cities in the State and City select fields.
form section using formik
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useNavigate, useParams } from 'react-router-dom';
import { Formik, Field, Form, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import AdminLeftNavbar from '../../components/AdminLeftNavbar';
import UploadImageToS3 from '../../components/UploadImageS3/UploadImageToS3';
import { clearMessage, setMessage } from '../../redux/messageSlice';
import { Country, State, City } from 'country-state-city';
import {
StyledHeaderWrapper,
StyledMainContainer,
StyledMainWrapper,
StyledBreadCrumbs,
StyledSectionTitle,
StyledContentWrapper,
OperatorVisibilityWrapper,
FormWrapper,
FormGroup,
DetailsWrapper,
AmenitiesParkingTypeWrapper,
OtherInfoWrapper,
OtherInfoHeader,
AddressWrapper,
AddressLocationWrapper,
AddressLocationGrandWrapper,
OperatorButtonWrapper,
AddressHeader,
CoordinatesHeader,
AddressContentWrapper,
OtherInfoSubWrapper,
} from './style';
import { createLocations } from '../../redux/locationsSlice';
import Loading from '../../components/Loading/Loading';
import FormikControl from '../../components/FormikComponents/FormikControl';
const NewLocations = () => {
const { operatorId } = useParams();
const [successful, setSuccessful] = useState(false);
const [showProfile, setShowProfile] = useState(true);
const operators = useSelector((state) => state.operators.data);
const operator = operators.filter(
(operator) => operator.id === parseInt(operatorId)
);
const { loading } = useSelector((state) => state.locations);
const dispatch = useDispatch();
const navigate = useNavigate();
const DEFAULT_COUNTRY = 'US';
useEffect(() => {
dispatch(clearMessage());
}, [dispatch]);
const availableAmenitiesSelection = [
'Smart Car',
'Self Park',
'Staffed',
'Paved',
'Driveaway',
'Mobile Pass',
'Twenty Four',
];
const availableParkingType = ['Outdoor', 'Covered', 'Indoor', 'UnCovered'];
const accessTypeOptions = [
{ key: 'Mobile Pay', value: 'mobile pay' },
{ key: 'Location Name', value: 'location name' },
];
const validationTypeOptions = [
{ key: 'By Plate', value: 'By Plate' },
{ key: 'Location Name', value: 'location name' },
];
const cityOption = [{ key: 'Abbeville', value: 'abbeville' }];
const updateTimezones = Country.getCountryByCode(DEFAULT_COUNTRY).timezones;
const initialValues = {
name: '',
bio: '',
operatedBy: '',
description: '',
images: '',
timeZone: '',
amenities: [],
parkingType: [],
numberOfSpaces: '',
operatorStreet: '',
accessType: '',
validationType: '',
address: {
street: '',
country: 'United States',
state: '',
city: '',
zip: '',
latitude: '',
longitude: '',
entranceLatitude: '',
entranceLongitude: '',
},
};
const validationSchema = Yup.object().shape({
name: Yup.string().required('Required!'),
bio: Yup.string().required('Required!'),
description: Yup.string().required('Required!'),
operatedBy: Yup.string().required('Required!'),
// images: Yup.string().required('Required!'),
timeZone: Yup.string().required('Required!'),
amenities: Yup.array().required('Required!'),
parkingType: Yup.array().required('Required!'),
numberOfSpaces: Yup.number()
.required('Required!')
.min(0, 'Number must be positive integer'),
accessType: Yup.string().required('Required!'),
validationType: Yup.string().required('Required!'),
address: Yup.object().shape({
street: Yup.string().required('Required!'),
// state: Yup.string().required('Required!'),
// city: Yup.string().required('Required!'),
zip: Yup.string()
.required('Required')
.matches(/^[0-9]+$/, 'Must be only digits')
.min(5, 'Must be exactly 5 digits')
.max(5, 'Must be exactly 5 digits'),
latitude: Yup.number().required('Required!'),
longitude: Yup.number().required('Required!'),
entranceLatitude: Yup.number().required('Required!'),
entranceLongitude: Yup.number().required('Required!'),
}),
});
const handleLocations = (formValue) => {
const {
name,
bio,
operatedBy,
description,
timeZone,
numberOfSpaces,
images,
amenities,
parkingType,
accessType,
validationType,
address: {
street,
state,
city,
zip,
latitude,
longitude,
entranceLatitude,
entranceLongitude,
},
} = formValue;
setSuccessful(false);
dispatch(
createLocations({
operatorId,
name,
bio,
description,
operatedBy,
timeZone,
numberOfSpaces,
amenities,
parkingType,
accessType,
validationType,
images,
address: {
street,
state,
city,
zip,
latitude,
longitude,
entranceLatitude,
entranceLongitude,
},
})
)
.unwrap()
.then(() => {
setSuccessful(true);
dispatch(setMessage('Signed up successfully.'));
navigate(`/admin/operators/${operatorId}/locations`);
})
.catch(() => {
setSuccessful(false);
});
};
const updatedStates = () =>
State.getStatesOfCountry(DEFAULT_COUNTRY).map((state) => ({
label: state.name,
value: state.isoCode,
...state,
}));
const updatedCities = (countryCode, isoCode) =>
City.getCitiesOfState(countryCode, isoCode).map((city) => ({
label: city.name,
value: city.isoCode,
...city,
}));
if (loading) {
return <Loading />;
}
return (
<StyledMainContainer>
<AdminLeftNavbar />
<StyledMainWrapper>
<StyledHeaderWrapper>
<StyledSectionTitle>Location Profile</StyledSectionTitle>
<StyledBreadCrumbs>
<Link to="/admin/operators">Operator Listings</Link> /
<span>General Info </span>
/ {' '}
<Link to={`/admin/operators/${operatorId}/locations`}>
Operator Locations
</Link>
</StyledBreadCrumbs>
</StyledHeaderWrapper>
<OperatorVisibilityWrapper>
<p className="operator">
Operator: <span>{operator.map((val) => val.name)}</span>
</p>
<div className="form-check form-switch form-check-reverse">
<input
className="form-check-input"
type="checkbox"
id="flexSwitchCheckReverse"
onClick={() => setShowProfile(!showProfile)}
/>
<label className="form-check-label" for="flexSwitchCheckReverse">
Public Profile Visibility:{' '}
<strong>{showProfile ? 'ON' : 'Hide'}</strong>
</label>
</div>
</OperatorVisibilityWrapper>
<StyledContentWrapper>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleLocations}
>
{(
// we need to use setFieldValue from Formik
{ values, setFieldValue, setValues }
) => (
<Form>
{!successful && (
<FormWrapper>
{showProfile ? (
<>
<h5>Profile</h5>
<FormGroup>
<FormikControl
control="input"
type="string"
label="Location Name"
name="name"
className="form-control field-width"
placeholder="V Lots"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="input"
type="string"
label="Bio"
name="bio"
className="form-control field-width"
placeholder="EV Charging Stations, Reservation only, Crystal Mountain US"
autoComplete="off"
/>
</FormGroup>
</>
) : null}
<h5>Hero Image</h5>
<FormGroup>
<FormikControl
control="textarea"
type="text"
label=""
name="images"
className="form-control hero-field-width"
src=""
/>
<Field name="images">
{({ form }) => (
<UploadImageToS3 setFieldValue={form.setFieldValue} />
)}
</Field>
</FormGroup>
<DetailsWrapper>
<h5>Details</h5>
<FormGroup>
<FormikControl
control="input"
type="string"
label="Operated By"
name="operatedBy"
className="form-control field-width"
placeholder="Crystal parking(Mineapolis, St.paul)"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="textarea"
label="Description"
name="description"
className="form-control field-width"
placeholder="Type your Description"
autoComplete="off"
/>
</FormGroup>
<AmenitiesParkingTypeWrapper>
<FormGroup>
<label htmlFor="amenities">
Amenities:
<Field
component="select"
name="amenities"
// You need to set the new field value
onChange={(evt) =>
setFieldValue(
'amenities',
[].slice
.call(evt.target.selectedOptions)
.map((option) => option.value)
)
}
multiple={true}
>
{availableAmenitiesSelection.map((amenity) => (
<option key={amenity} value={amenity}>
{amenity}
</option>
))}
</Field>
<ErrorMessage
name="amenities"
component="div"
className="alert alert-danger"
/>
</label>
</FormGroup>
<FormGroup>
<label htmlFor="parkingType">
Parking Type:
<Field
component="select"
name="parkingType"
// You need to set the new field value
onChange={(evt) =>
setFieldValue(
'parkingType',
[].slice
.call(evt.target.selectedOptions)
.map((option) => option.value)
)
}
multiple={true}
>
{availableParkingType.map((parking) => (
<option key={parking} value={parking}>
{parking}
</option>
))}
</Field>
<ErrorMessage
name="parkingType"
component="div"
className="alert alert-danger"
/>
</label>
</FormGroup>
</AmenitiesParkingTypeWrapper>
</DetailsWrapper>
<OtherInfoHeader>
<h5>Other Info</h5>
</OtherInfoHeader>
<OtherInfoWrapper>
<OtherInfoSubWrapper>
<FormGroup>
<FormikControl
control="input"
type="number"
label="No. Of Spaces"
name="numberOfSpaces"
className="form-control field-width mb-2"
placeholder="20"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="select"
label="Access Type"
name="accessType"
options={accessTypeOptions}
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="select"
label="Validation Type"
name="validationType"
options={validationTypeOptions}
autoComplete="off"
/>
</FormGroup>
</OtherInfoSubWrapper>
<FormGroup>
<FormikControl
control="select"
label="Time Zone"
name="timeZone"
options={updateTimezones.map((timezone) => ({
value: timezone.zoneName,
key: timezone.zoneName,
}))}
placeholder="Eastern standard Time"
/>
</FormGroup>
</OtherInfoWrapper>
<AddressWrapper>
<AddressHeader>
<h5>Address</h5>
</AddressHeader>
<AddressContentWrapper>
<FormGroup>
<FormikControl
control="input"
type="string"
label="Street"
name="address.street"
className="form-control field-width mb-2"
placeholder="City Parking - 113 6th Ave..."
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="select"
label="State"
id="state"
name="address.state"
className="field-width"
options={updatedStates(
values.country ? values.country.value : null
).map((state) => ({
value: state.isoCode,
key: state.name,
}))}
value={values.state}
onChange={(e) => {
e.preventDefault();
setValues(
{ state: e.target.value, city: null },
false
);
}}
/>
</FormGroup>
<FormGroup>
<FormikControl
control="select"
label="City"
id="city"
name="address.city"
className="field-width"
options={
values.state
? updatedCities(
DEFAULT_COUNTRY,
values.state
).map((city) => ({
value: city.isoCode,
key: city.name,
}))
: cityOption
}
value={values.city}
onChange={(e) => {
e.preventDefault();
setFieldValue('city', e.target.value);
}}
/>
</FormGroup>
</AddressContentWrapper>
<FormGroup>
<FormikControl
control="input"
type="string"
label="Zip Code"
name="address.zip"
className="form-control field-width"
placeholder="45545"
autoComplete="off"
/>
</FormGroup>
</AddressWrapper>
<AddressLocationGrandWrapper>
<CoordinatesHeader>
<h5>Coordinates</h5>
</CoordinatesHeader>
<AddressLocationWrapper>
<FormGroup>
<FormikControl
control="input"
type="number"
label="Latitude"
name="address.latitude"
className="form-control field-address-location"
placeholder="4.3566"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="input"
type="number"
label="Longitude"
name="address.longitude"
className="form-control field-address-location"
placeholder="6.779705"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="input"
type="number"
label="Entrance Location Latitude"
name="address.entranceLatitude"
className="form-control field-address-location"
placeholder="6.779701"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<FormikControl
control="input"
type="number"
label="Entrance Location Longitude"
name="address.entranceLongitude"
className="form-control field-address-location"
placeholder="6.779701"
autoComplete="off"
/>
</FormGroup>
</AddressLocationWrapper>
</AddressLocationGrandWrapper>
<OperatorButtonWrapper>
<button
type="submit"
className="operator-cancel"
onClick={() =>
navigate(`/admin/operators/${operatorId}/locations`)
}
>
Cancel
</button>
<button type="submit">Save</button>
</OperatorButtonWrapper>
</FormWrapper>
)}
</Form>
)}
</Formik>
</StyledContentWrapper>
</StyledMainWrapper>
</StyledMainContainer>
);
};
export default NewLocations;
select function
import { ErrorMessage, Field } from 'formik';
import TextError from '../TextError/TextError';
const Select = (props) => {
const { label, name, options, ...rest } = props;
return (
<div>
<label htmlFor={name}>{label}</label>
<Field as="select" id={name} name={name} {...rest}>
{options.map((option) => {
return (
<option key={option.value} value={option.value}>
{option.key}
</option>
);
})}
</Field>
<ErrorMessage name={name} component={TextError} />
</div>
);
};
export default Select;
FormikControl component
import CheckboxGroup from './CheckboxGroup/CheckboxGroup';
import DatePicker from './DatePicker/DatePicker';
import Input from './InputForm/Input';
import RadioButtons from './RadioButtons/RadioButtons';
import Select from './SelectForm/Select';
import Textarea from './Textarea/Textarea';
const FormikControl = (props) => {
const { control, ...rest } = props;
switch (control) {
case 'input':
return <Input {...rest} />;
case 'textarea':
return <Textarea {...rest} />;
case 'select':
return <Select {...rest} />;
case 'radio':
return <RadioButtons {...rest} />;
case 'checkbox':
return <CheckboxGroup {...rest} />;
case 'date':
return <DatePicker {...rest} />;
default:
return null;
}
};
export default FormikControl;
Any idea on how I can prevent this uncontrolled event triggerring is humbly welcome.
I am trying to access the college array but it is throwing an error saying "college .map is not a function". I want to loop over the array. But when I checked the type of college it is saying that type of(college) is an object. I have declared the college state as array. I am not getting how can I loop over the college array to display the information. Thanks in advance.
Here's my code:
import React, { useState, useContext,useEffect } from 'react'
import {
Form,
FormGroup,
Label,
Input,
Button,
Col
} from 'reactstrap'
import { GlobalContext } from '../context/GlobalState'
import { useHistory } from 'react-router-dom'
import { v4 as uuid } from 'uuid'
import axios from 'axios'
const AddUser = () => {
const { addUser } = useContext(GlobalContext)
const history = useHistory()
const [fullname, setFullName] = useState('')
const [birthdate,setBirthDate]=useState()
const [address,setAddress]=useState('')
const [gender,setGender]=useState('')
const [college,setCollege]=useState([])
const handleSubmit = () => {
const newUser = {
id: uuid(),
fullname,
birthdate,
address,
college,
gender
}
addUser(newUser)
history.push('/userslist')
}
useEffect(() => {
async function fetchData(name){
const response = await axios.get(`http://universities.hipolabs.com/search`,{
params:{name}
}
)
console.log(response.data)
setCollege({ college: response.data })
}
fetchData('Middle')
}, [])
return (
<Form onSubmit={handleSubmit}>
<FormGroup>
<Label>Name</Label>
<Input type='text' placeholder='Enter Name'
value={fullname}
onChange={(e) => setFullName(e.target.value)}
name='fullname'
/>
</FormGroup>
<FormGroup>
<Label>BirthDate</Label>
<Input type='date'
placeholder='Enter BirthDate'
value={birthdate} onChange={(e)=>setBirthDate(e.target.value)}
name='birthdate'
/>
</FormGroup>
<FormGroup>
<Label>Address</Label>
<Input type='textarea'
placeholder='Enter Address'
value={address} onChange={(e)=>setAddress(e.target.value)}
name='address' />
</FormGroup>
<FormGroup row>
<Label for="exampleSelect" sm={2}>Select College Name</Label>
<Col sm={10}>
<Input type="select" name="select" id="exampleSelect" onChange={(e)=>e.target.value}>
<option value='' disabled hidden>College List</option>
{college ?
college.map((col, index) => {
return <option value={col.name} key={index + 1}>{col.name}</option>
}):null
}
</Input>
</Col>
</FormGroup>
<FormGroup tag="fieldset">
<Label>Please Select Gender</Label>
<FormGroup>
<Label>
<Input type='radio' name='gender'
onChange={(e)=>setGender(e.target.value)}
value='Male'
checked={gender === "Male"}
/>
Male
</Label>
</FormGroup>
<FormGroup>
<Label >
<Input type='radio' name='gender'
onChange={(e)=>setGender(e.target.value)}
value='Female'
checked={gender === "Female"} />
Female
</Label>
</FormGroup>
<FormGroup>
<Label>
<Input type='radio' name='gender'
onChange={(e)=>setGender(e.target.value)}
value='Other'
checked={gender === 'Other'} />
Other
</Label>
</FormGroup>
</FormGroup>
<Button type='submit'>Submit</Button>
</Form>
)
}
export default AddUser
your line: setCollege({ college: response.data }) will replace the default array you set in state. Setters for useStates always replaces the entire state.
do setCollege([...college, {college: response.data}]) instead and it should work fine
You are setting college as an object here
setCollege({ college: response.data })
Change that to
setCollege([{ college: response.data }])
Or append like
setCollege([...college, { college: response.data }])
and that will fix it.
You are directly placing the response object in your college state. .map() is function of array, so response from your API might not be in array format. Convert it into array then you can loop through array with map().
I have a component with a form inside. The fields of the form rely on the state of the component that is updated within the onChange methods. Upon clicking the submit button, the component sends the form fields to the parent component, then updates the store. When the onChange method is called, the error "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."
I can't find the reason why a loop occurs inside the "MemberForm" component.
By debugging, however, I understood that at the change of state (not to the onChange function) the parent component "AggiungiSocio" is also rendered. However, the status of the "MemberForm" component is in no way related to the "AggiungiSocio" component.
What is causing this unexpected rendering? How can I avoid it?
The strange thing is that I also use the "MemberForm" component in the "ModificaSocio" component where I pass the form fields as properties. In this component "ModificaSocio" everything works correctly.
To recap
AddSocio -> MemberForm doesn't work
EditSocio -> MemberForm works
AggiungiSocio
import React, { Component, Fragment } from "react";
import {
Row,
Card,
CardBody,
CardTitle,
CardSubtitle,
} from "reactstrap";
import IntlMessages from "../../../helpers/IntlMessages";
import { Colxx } from "../../../components/common/CustomBootstrap";
import MemberForm from "../../../containers/form/MemberForm";
import {injectIntl} from "react-intl";
import {connect} from "react-redux";
import {addMemberDetail} from "../../../redux/memberDetail/actions";
import {Socio} from "../../../models/socio";
class AggiungiSocio extends Component {
constructor(props) {
super(props);
}
aggiungiSocio = (socio) => {
console.log(socio);
this.props.addMemberDetail(socio);
};
render() {
console.log("render aggiungi socio");
const {
loading
} = this.props.memberDetail;
return (
<Fragment>
<Row className="mb-4">
<Colxx xxs="12">
<Card>
<CardBody>
<CardTitle>
<IntlMessages id="pages.member.add-member"/>
</CardTitle>
<CardSubtitle>
<IntlMessages id="pages.member.add-member-description"/>
</CardSubtitle>
{loading ? (
<MemberForm onSubmit={this.aggiungiSocio}/>
) : (
<div className="loading"/>
)
}
</CardBody>
</Card>
</Colxx>
</Row>
</Fragment>
)
}
}
const mapStateToProps = ({memberDetail}) => {
return {
memberDetail
};
};
export default injectIntl(
connect(
mapStateToProps,
{
addMemberDetail
}
)(AggiungiSocio)
);
ModificaSocio
import React, { Component, Fragment } from "react";
import {
Row,
Card,
CardBody,
CardTitle
} from "reactstrap";
import IntlMessages from "../../../helpers/IntlMessages";
import { Colxx } from "../../../components/common/CustomBootstrap";
import "react-datepicker/dist/react-datepicker.css";
import MemberForm from "../../../containers/form/MemberForm";
import {injectIntl} from "react-intl";
import {connect} from "react-redux";
import {getMemberDetail, editMemberDetail} from "../../../redux/memberDetail/actions";
class ModificaSocio extends Component {
constructor(props) {
super(props);
this.state = {
};
}
modificaSocio = (socio) => {
this.props.editMemberDetail(socio);
}
render() {
const {
member,
loading
} = this.props.memberDetail;
return (
<Fragment>
<Row className="mb-4">
<Colxx xxs="12">
<Card>
<CardBody>
<CardTitle>
<IntlMessages id="pages.member.edit-member"/>
</CardTitle>
{loading ? (
<MemberForm member={member} onSubmit={this.modificaSocio}/>
): (
<div className="loading" />
)
}
</CardBody>
</Card>
</Colxx>
</Row>
</Fragment>
)
}
}
const mapStateToProps = ({memberDetail}) => {
return {
memberDetail
};
};
export default injectIntl(
connect(
mapStateToProps,
{
editMemberDetail,
getMemberDetail
}
)(ModificaSocio)
);
MemberForm
import React, { Component } from "react";
import {
Input,
FormGroup,
Label,
Button,
Form
} from "reactstrap";
import IntlMessages from "../../helpers/IntlMessages";
import DatePicker from "react-datepicker";
import "react-tagsinput/react-tagsinput.css";
import "react-datepicker/dist/react-datepicker.css";
import "rc-switch/assets/index.css";
import "rc-slider/assets/index.css";
import "react-rater/lib/react-rater.css";
import { Colxx } from "../../components/common/CustomBootstrap";
import Select from "react-select";
import CustomSelectInput from "../../components/common/CustomSelectInput";
import moment from "moment";
import {Socio} from "../../models/socio";
class MemberForm extends Component {
constructor(props) {
super(props);
moment.locale("it");
this.state = {
member: (typeof this.props.member === 'undefined') ? Socio : this.props.member,
selectQualifica: [{
label: (typeof this.props.member === 'undefined') ? '' : this.props.member.qualifica,
value: (typeof this.props.member === 'undefined') ? '' : this.props.member.qualifica
}],
selectTitolo: [{
label: (typeof this.props.member === 'undefined') ? '' : this.props.member.titolo,
value: (typeof this.props.member === 'undefined') ? '' : this.props.member.titolo
}],
dataDiNascita: moment((typeof this.props.member === 'undefined') ? null : this.props.member.dataDiNascita, "DD/MM/YYYY"),
dataRichiestaIscrizione: moment((typeof this.props.member === 'undefined') ? null : this.props.member.dataRichiestaIscrizione, "DD/MM/YYYY"),
dataAccettazione: moment((typeof this.props.member === 'undefined') ? null : this.props.member.dataAccettazione, "DD/MM/YYYY")
}
}
handleChangeQualifica = qualifica => {
this.setState({
member: {
...this.state.member,
qualifica: qualifica.value
},
selectQualifica: [{
label: qualifica.value,
value: qualifica.value
}]
});
}
handleChangeTitolo = titolo => {
this.setState({
member: {
...this.state.member,
titolo: titolo.value
},
selectTitolo: [{
label: titolo.value,
value: titolo.value
}]
});
}
handlerChange = event => {
const name = event.target.name;
const value = event.target.value;
if (value!=this.state.member[name]) {
console.log("il valore è cambiato, aggiorno stato");
console.log(value, this.state.member[name]);
this.setState({
member: {
...this.state.member,
[name]: value
}
});
} else {
console.log("il valore è lo stesso!, non aggiorno stato");
console.log(value, this.state.member[name]);
}
}
handleChangeDataNascita = date => {
this.setState({
member: {
...this.state.member,
dataDiNascita: date.format("DD/MM/YYYY")
},
dataDiNascita: date
});
};
handleChangeDataRichiestaIscrizione = date => {
this.setState({
member: {
...this.state.member,
dataRichiestaIscrizione: date.format("DD/MM/YYYY")
},
dataRichiestaIscrizione: date
});
};
handleChangeDataAccettazione = date => {
this.setState({
member: {
...this.state.member,
dataAccettazione: date.format("DD/MM/YYYY")
},
dataAccettazione: date
});
};
handleSubmit = () => {
this.props.onSubmit(this.state.member);
}
render () {
console.log("render memberform");
const {
member,
selectQualifica,
selectTitolo,
dataDiNascita,
dataRichiestaIscrizione,
dataAccettazione
} = this.state;
return (
(member &&
<Form>
<FormGroup row>
<Colxx sm={2}>
<FormGroup>
<Label for="memberTitle">
<IntlMessages id="member.idSocio"/>
</Label>
<Input
disabled
readOnly
type="text"
name="memberId"
id="memberId"
defaultValue={member.numeroSocio}
/>
</FormGroup>
</Colxx>
<Colxx sm={2}>
<FormGroup>
<Label for="memberTitle">
<IntlMessages id="member.title"/>
</Label>
<Select
components={{Input: CustomSelectInput}}
className="react-select"
classNamePrefix="react-select"
name="form-field-name"
id="memberTitle"
options={[
{label: "Sig", value: "Sig", key: 0},
{label: "Sig.ra", value: "Sig.ra", key: 1}
]}
value={selectTitolo}
onChange={this.handleChangeTitolo.bind(this)}
/>
</FormGroup>
</Colxx>
<Colxx sm={3}>
<Label for="memberName">
<IntlMessages id="member.name"/>
</Label>
<Input
type="text"
name="nome"
id="memberName"
value={member.nome}
onChange={this.handlerChange.bind(this)}
/>
</Colxx>
<Colxx sm={3}>
<Label for="memberSurname">
<IntlMessages id="member.surname"/>
</Label>
<Input
type="text"
name="cognome"
id="memberSurname"
value={member.cognome}
onChange={this.handlerChange.bind(this)}
/>
</Colxx>
<Colxx sm={2}>
<Label for="memberSurname">
<IntlMessages id="member.birthday"/>
</Label>
<DatePicker
dateFormat="DD/MM/YYYY"
selected={dataDiNascita}
onChange={this.handleChangeDataNascita.bind(this)}
id="memberBirthday"/>
</Colxx>
</FormGroup>
<FormGroup row>
<Colxx sm={3}>
<FormGroup>
<Label for="birth-comune">
<IntlMessages id="member.birth-comune"/>
</Label>
<Input
type="text"
name="comuneDiNascita"
id="birth-comune"
onChange={this.handlerChange.bind(this)}
value={member.comuneDiNascita}
/>
</FormGroup>
</Colxx>
<Colxx sm={2}>
<FormGroup>
<Label for="birth-provincia">
<IntlMessages id="member.birth-provincia"/>
</Label>
<Input
type="text"
name="provinciaDiNascita"
id="birth-provincia"
onChange={this.handlerChange.bind(this)}
value={member.provinciaDiNascita}
/>
</FormGroup>
</Colxx>
<Colxx sm={4}>
<FormGroup>
<Label for="residential-address">
<IntlMessages id="member.residential-address"/>
</Label>
<Input
type="text"
name="indirizzoDiResidenza"
id="residential-address"
onChange={this.handlerChange.bind(this)}
value={member.indirizzoDiResidenza}
/>
</FormGroup>
</Colxx>
<Colxx sm={3}>
<FormGroup>
<Label for="codice-fiscale">
<IntlMessages id="member.codice-fiscale"/>
</Label>
<Input
type="text"
name="codiceFiscale"
id="codice-fiscale"
onChange={this.handlerChange.bind(this)}
value={member.codiceFiscale}
/>
</FormGroup>
</Colxx>
</FormGroup>
<FormGroup row>
<Colxx sm={3}>
<FormGroup>
<Label for="data-richiesta-iscrizione">
<IntlMessages id="member.data-richiesta-iscrizione"/>
</Label>
<DatePicker
dateFormat="DD/MM/YYYY"
selected={dataRichiestaIscrizione}
onChange={this.handleChangeDataRichiestaIscrizione.bind(this)}
id="data-richiesta-iscrizione"/>
</FormGroup>
</Colxx>
<Colxx sm={3}>
<FormGroup>
<Label for="data-accettazione">
<IntlMessages id="member.data-accettazione"/>
</Label>
<DatePicker
dateFormat="DD/MM/YYYY"
selected={dataAccettazione}
onChange={this.handleChangeDataAccettazione.bind(this)}
id="data-accettazione"/>
</FormGroup>
</Colxx>
<Colxx sm={3}>
<FormGroup>
<Label for="payment">
<IntlMessages id="member.payment"/>
</Label>
<Input
type="text"
name="quotaVersata"
id="payment"
onChange={this.handlerChange.bind(this)}
value={member.quotaVersata}
/>
</FormGroup>
</Colxx>
<Colxx sm={3}>
<FormGroup>
<Label for="qualification">
<IntlMessages id="member.qualification"/>
</Label>
<Select
components={{Input: CustomSelectInput}}
className="react-select"
classNamePrefix="react-select"
name="form-field-name"
id="qualification"
options={[
{label: "Socio", value: "Socio", key: 0},
{label: "Socio Fondatore", value: "Socio Fondatore", key: 1},
{label: "Consigliere", value: "Consigliere", key: 2}
]}
value={selectQualifica}
onChange={this.handleChangeQualifica}
/>
</FormGroup>
</Colxx>
</FormGroup>
<Button color="primary" onClick={this.handleSubmit.bind(this)}>
<IntlMessages id="pages.member.save"/>
</Button>
</Form>
)
)
}
}
export default React.memo(MemberForm);
You can't pass params to a function outside a function.
Correct:
< ... onChange={function} />
Not correct:
< ... onChange={function(params)} />
To use params you need to nest you function inside anonymous function :
< ... onChange={() => function(params)} />
In you case you want to write this :
< ... onChange={() => this.handlerChange.bind(this)} />
In my register form my radio buttons don't work, when I press the create button nothing happens, it doesn't activate the onSubmit function, but if I remove them it gets activated.
Here is my code:
import React from "react";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
const male = props => (
<input type="radio" value="male" name="gender" {...props} />
);
const female = props => (
<input type="radio" value="female" name="gender" {...props} />
);
export class RegisterPage extends React.Component {
render() {
let { initialValues } = this.state;
return (
<div>
<div>
<h1>Sign Up</h1>
<Formik
initialValues={initialValues}
onSubmit={(
{ email, password, gender },
{ setStatus, setSubmitting }
) => {
setStatus();
authenticationservice.newuser({ email, password, gender }).then(
user => {
const { from } = this.props.location.state || { from: { pathname: "/" } `};`
this.props.history.push(from);
},
error => {
setSubmitting(false);
setStatus(error);
}
);
}}
render={({ errors, status, touched, isSubmitting }) => (
<Form>
<div>
<label htmlFor="email">Email Address</label>
<Field name="email" type="text" />
<ErrorMessage name="email" component="div" />
</div>
<div>
<label htmlFor="gender">Gender</label>
<label>Male</label>
<Field name="gender" as={male} />
<label>Female</label>
<Field name="gender" as={female} />
</div>
<div>
<button type="submit" disabled={isSubmitting}>
Create
</button>
{isSubmitting && <img alt="" src="data:image" />}
</div>
{status && <div>{status}</div>}
</Form>
)}
/>
</div>
</div>
);
}
}
I'm not sure what happens because I don't get any error codes, there is just not happening anything
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)