I'm working on uploading images on my react app, the idea is to save the images in my object, send it to my back end and upload them to clodinary but right now i cant seem to put the files in my object array:
Component:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import PropTypes from 'prop-types';
import Dropzone from 'react-dropzone';
import {
Grid,
Row,
Col,
FormControl,
FormGroup,
Button,
ButtonToolbar,
Label
} from 'react-bootstrap';
import {Dwelling} from '../../../model';
import {requestSaveDwelling} from '../../../actions';
class New3 extends Component {
static propTypes = {
requestSaveDwelling: PropTypes.func.isRequired,
history: PropTypes.shape({
push: PropTypes.func.isRequired
}).isRequired,
dwelling: PropTypes.shape({})
};
static defaultProps = {
dwelling: new Dwelling()
};
constructor(props) {
super(props);
this.state = {dwelling: new Dwelling()};
if (this.props.dwelling) {
this.state = this.props;
}
}
handleChange({target: {id, value}}) {
this.setState(
state => ({
dwelling: (Object.assign(state.dwelling, {[id]: value}))
})
);
}
handleDrop(file) {
this.setState(
state => ({
dwelling: (Object.assign(state.dwelling.images, file))
})
);
}
handleSubmit() {
const {dwelling} = this.state;
this.props.requestSaveDwelling(dwelling);
this.props.history.push('/dwellings/latest');
}
render() {
const {dwelling} = this.state;
console.log(dwelling);
return (
<Grid className="animated fadeIn">
<Row>
<Col sm={12}>
<h2>Carga de Imágenes</h2>
<Dropzone
onDrop={this.handleDrop}
multiple
accept="image/*"
>
<p>Arrastre Imagenes aquí, o haga click para seleccionar imagenes.</p>
</Dropzone>
{this.state.dwelling.images.length > 0 ?
<div>
<h2>Uploading {this.state.dwelling.images.length} files...</h2>
<div>{this.state.dwelling.images.map(file => <img src={file.preview}/>)}</div>
</div> : null}
</Col>
</Row>
<Row>
<Col sm={12}>
<FormGroup controlId="occupationStatus">
<Label>Estado Ocupacional</Label>
<FormControl
componentClass="select"
value={dwelling.occupationStatus}
placeholder="Seleccione"
onChange={e => this.handleChange(e)}
>
<option disabled label="Seleccione"/>
<option value="Disponible" label="Disponible"/>
<option value="Alquilada" label="Alquilada"/>
<option value="Vendida" label="Vendida"/>
<option value="Reservada" label="Reservada"/>
<option value="Suspendida" label="Suspendida"/>
</FormControl>
</FormGroup>
</Col>
</Row>
<Row>
<Col sm={12}>
<h2>Descripción General</h2>
<FormGroup controlId="generalDescription">
<FormControl
componentClass="textarea"
value={dwelling.generalDescription}
onChange={e => this.handleChange(e)}
placeholder="Escriba una Descripcion general"
/>
</FormGroup>
</Col>
</Row>
<Row>
<Col sm={12}>
<h2>Descripción Privada</h2>
<FormGroup controlId="privateDescription">
<FormControl
componentClass="textarea"
value={dwelling.privateDescription}
onChange={e => this.handleChange(e)}
placeholder="Escriba una Descripcion privada"
/>
</FormGroup>
</Col>
</Row>
<Row>
<Col sm={6}>
<ButtonToolbar className="pull-left">
<Button href="#/dwellings/new2">Atrás</Button>
</ButtonToolbar>
</Col>
<Col sm={6}>
<ButtonToolbar className="pull-right">
<Button onClick={() => this.handleSubmit()}>Guardar</Button>
</ButtonToolbar>
</Col>
</Row>
</Grid>
);
}
}
export default connect(
state => ({
dwelling: state.dwelling.dwelling
}),
dispatch => ({
requestSaveDwelling: dwelling => dispatch(requestSaveDwelling(dwelling))
})
)(New3);
object model:
export default class Dwelling {
_id = undefined;
publicationType = '';
address = {
address: '',
latitude: null,
longitude: null
}
images = [];
constructor(obj) {
Object.assign(this, obj);
}
}
when i try to upload an image i get this error:
New3.js:51 Uncaught TypeError: Cannot read property 'images' of undefined
at Dropzone. (New3.js:51)
This is my example of my previewer with FileReader. However, this is only one file so you might want to adjust it for your app.
class Preview extends React.Component {
constructor(props) {
super(props);
this.state = {
previewSrc: ''
}
}
handlePreview = (e) => {
e.preventDefault();
let file = e.target.files[0];
let reader = new FileReader();
if (e.target.files.length === 0) {
return;
}
reader.onloadend = (e) => {
this.setState({
previewSrc: [reader.result]
});
}
reader.readAsDataURL(file);
}
render() {
return (
<div id="previewer">
<input type="file" onChange={this.handlePreview} />
<h1>Preview</h1>
<img src={this.state.previewSrc} alt="" />
</div>
);
}
}
ReactDOM.render(<Preview/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
<div id="root"></div>
References:
https://developer.mozilla.org/en-US/docs/Web/API/FileReader
Related
I have an application where I have used redux-form version 5.3.6, now I want to migrate from it to react-final-form. As I have mostly used Redux to manage form data while using redux-form, now while I am migrating, I find it very hard to integrate my redux codes with the final form implementation mentioned in the migration doc: https://final-form.org/docs/react-final-form/migration/redux-form
I am finding it difficult to use my mapStateToProps, mapDispatchToProps, and other redux-related functions with the final-form implementation.
Here is my current form container code from where form data is getting connected with redux via redux-form
import React from 'react';
import PropTypes from 'prop-types';
import { reduxForm } from 'redux-form';
import moment from 'moment';
import { startFromCart, endFromCart } from '../../lib/datesFromCart';
import { validateAllSearchParams } from './helpers/validate';
import cartOverride from './helpers/cartOverride';
import deepClone from '../../lib/deepClone';
import SearchForm from './SearchForm';
import searchAccountValues from '../../helpers/searchAccountValues';
import { createCarSearch } from '../../actions/car';
import { resetApp } from '../../actions/common';
const initialFields = [
'pickUpDate',
'dropOffDate',
'pickUpTime',
'dropOffTime',
'airportSearch',
'pickUpLocationAirportCode',
'pickUpGeoLocationAddress',
'pickUpGeoLocationLatitude',
'pickUpGeoLocationLongitude',
'dropOffLocationAirportCode',
'dropOffGeoLocationAddress',
'dropOffGeoLocationLatitude',
'dropOffGeoLocationLongitude',
'driverAge',
'vendorPreferences.codes',
'vendorPreferences.type',
];
const reduxFormConfig = {
form: 'carSearchForm',
destroyOnUnmount: false,
fields: [],
};
const mapStateToProps = (state, ownProps) => {
const { adults } = state.common.travelerCount;
const fields = deepClone(initialFields);
const cartHasCar = state.common.cart.items.some(item => item.type === 'car');
if (ownProps.searchAccounts && ownProps.searchAccounts.cars) {
Object.keys(ownProps.searchAccounts.cars).forEach(key => fields.push(`accountSettings.${key}`));
}
const startDateFromCart = startFromCart(state);
const endDateFromCart = endFromCart(state);
let vendorsPreferences = []
if (ownProps.globalPolicyPreferences.carRentalCompanies.length > 0) {
vendorsPreferences = ownProps.globalPolicyPreferences.carRentalCompanies
}
const defaultSearchParams = {
accountSettings: searchAccountValues(ownProps.searchAccounts.cars),
airportSearch: false,
driverAge: ownProps.currentUserAge || 32,
pickUpDate:
startDateFromCart ||
state.cars.defaultCarSearchParams.pickUpDate ||
moment()
.add(7, 'days')
.startOf('day')
.format('YYYY-MM-DD'),
dropOffDate: endDateFromCart || state.cars.defaultCarSearchParams.dropOffDate,
pickUpTime: '12:00',
dropOffTime: '11:00',
vendorPreferences: state.cars.defaultCarSearchParams.vendorPreferences || {
codes: vendorsPreferences,
type: 'exclusive',
},
};
const searchForm = state.form.carSearchForm;
const preselectTravelers = state.common.preselectTravelers.travelers;
const cartHasItems = state.common.cart.items.length > 0;
return {
fields,
cartHasCar,
initialValues: {
...defaultSearchParams,
...ownProps.searchParams,
...state.cars.defaultCarSearchParams,
...cartOverride(adults),
},
validate: validateAllSearchParams(ownProps.availableChannels.cars, ownProps.minHoursInFuture),
defaultSearchParams,
preselectTravelers,
searchForm,
cartHasItems,
travelerLoggedIn: ownProps.travelerLoggedIn,
};
};
const mapDispatchToProps = dispatch => ({
createCarSearch: (params, callbacks) => dispatch(createCarSearch(params, callbacks)),
resetApp: () => dispatch(resetApp('/cars')),
});
const ReduxForm = reduxForm(reduxFormConfig, mapStateToProps, mapDispatchToProps)(SearchForm);
const SearchFormWithContext = (props, context) => <ReduxForm {...props} {...context} />;
SearchFormWithContext.contextTypes = {
availableChannels: PropTypes.object,
searchAccounts: PropTypes.object,
laymanMode: PropTypes.bool,
currency: PropTypes.string,
callbackParams: PropTypes.object,
minHoursInFuture: PropTypes.number.isRequired,
currentUserAge: PropTypes.number.isRequired,
globalPolicyPreferences: PropTypes.shape({
airlines: PropTypes.arrayOf(PropTypes.number).isRequired,
carRentalCompanies: PropTypes.arrayOf(PropTypes.number).isRequired,
}).isRequired,
};
export default SearchFormWithContext;
The component is
import React, { useEffect, Fragment, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import moment from 'moment';
import { I18nText } from '#wtag/react-comp-lib';
import { Menu, MenuItem } from '#wtag/rcl-menu';
import Alert from '#wtag/rcl-alert';
import SelectBox from '#wtag/rcl-select-box';
import Input from '#wtag/rcl-input';
import Button from '#wtag/rcl-button';
import Icon from '#wtag/rcl-icon';
import LocationSearchInput from 'sharedWebpack/LocationSearchInput';
import DatePickerWithFocusedStateWrapper from 'sharedWebpack/DatePickerWithFocusedStateWrapper';
import TimeSelectBox from 'sharedWebpack/TimeSelectBox';
import availableChannelsShape from '../../shapes/availableChannels';
import noChannelSelected from '../../lib/helpers/noChannelSelected';
import showInvalidDateError from '../../helpers/showInvalidDateError';
import history from '../../lib/history';
import { showError } from '../../actions/common';
import AirportAutoComplete from '../AirportAutoComplete';
import VendorSelectBox from './VendorSelectBox';
import SearchAccounts from '../../components/SearchAccounts';
import searchAccountsShape from '../../shapes/searchAccounts';
import PreselectTravelers from '../PreselectTravelers';
import preparePreselectTravelers from '../PreselectTravelers/preparePreselectTravelers';
import useSearchFormContainer from '../../helpers/hooks/useSearchFormContainer';
const SearchForm = props => {
const {
fields: {
airportSearch,
pickUpLocationAirportCode,
dropOffLocationAirportCode,
pickUpGeoLocationAddress,
pickUpGeoLocationLatitude,
pickUpGeoLocationLongitude,
dropOffGeoLocationAddress,
dropOffGeoLocationLatitude,
dropOffGeoLocationLongitude,
pickUpDate,
dropOffDate,
pickUpTime,
dropOffTime,
driverAge,
vendorPreferences,
accountSettings,
},
submitting,
handleSubmit,
laymanMode,
availableChannels,
cartHasCar,
createCarSearch,
defaultSearchParams,
initializeForm,
globalPolicyPreferences,
resetApp,
searchForm,
preselectTravelers,
setIsCollapsed,
cartHasItems,
formWithoutChildren,
resetBookingAppContext,
travelerLoggedIn,
travelArrangerPresent,
} = props;
const [isLocationMenuVisible, setIsLocationMenuVisible] = useState(false);
const [isVendorMenuVisible, setIsVendorMenuVisible] = useState(false);
const [isAgeMenuVisible, setIsAgeMenuVisible] = useState(false);
const onLocationMenuOutsideClick = () => {
setIsLocationMenuVisible(false);
};
const onVendorMenuOutsideClick = () => {
setIsVendorMenuVisible(false);
};
const onAgeMenuOutsideClick = () => {
setIsAgeMenuVisible(false);
};
const showCarFormContainer = useSearchFormContainer();
const filterTypeOptions = [
{
value: 'preferred',
label: I18n.t('components.ibe.search_form.car.label.vendor_preferences_type.preferred'),
isDisabled: laymanMode
? availableChannels.cars.includes('galileo')
: accountSettings && accountSettings.galileo && accountSettings.galileo.value.length > 0,
},
{
value: 'exclusive',
label: I18n.t('components.ibe.search_form.car.label.vendor_preferences_type.exclusive'),
},
];
const disableVendorPreference = globalPolicyPreferences.carRentalCompanies.length > 0;
const isNoChannelSelected = noChannelSelected(
laymanMode,
availableChannels,
accountSettings,
'cars',
);
const onPickUpLocationSelect = (address, latitude, longitude) => {
if (!dropOffGeoLocationAddress.touched) {
dropOffGeoLocationAddress.onChange(address);
dropOffGeoLocationLatitude.onChange(latitude);
dropOffGeoLocationLongitude.onChange(longitude);
}
};
const onPickUpAirportChange = airport => {
pickUpLocationAirportCode.onChange(airport);
if (!dropOffLocationAirportCode.touched) {
dropOffLocationAirportCode.onChange(airport);
}
};
const onPickUpDateChange = date => {
if (date === '') {
pickUpDate.onChange('');
} else {
const pickUpDateValue = moment.utc(date);
pickUpDate.onChange(pickUpDateValue.format());
if (!dropOffDate.touched) {
const nextValue = pickUpDateValue.add(7, 'days').format();
dropOffDate.onChange(nextValue);
}
}
};
const handleFormSubmission = (values, dispatch, ownProps) => {
history.push('/cars/searching');
createCarSearch(
{
...values,
currency: ownProps.currency,
preselectTravelers: preparePreselectTravelers(preselectTravelers),
},
ownProps.callbackParams,
)
.then(data => data && data.id && history.push(`/cars/searches/${data.id}`))
.catch(error => dispatch(showError('Search failed for unknown reasons', error)));
setIsCollapsed();
};
const resetToDefaultSearchParams = () => {
initializeForm(defaultSearchParams);
};
const findTimeSelectBoxValue = value => {
if (value === 'anytime') {
return { label: <I18nText id="departure_times.anytime" />, value };
}
return { label: value, value };
};
const onDropOffDateChange = date => {
if (date === '') {
dropOffDate.onChange('');
} else if (date) {
dropOffDate.onChange(moment.utc(date).format());
}
};
const minDropOffDate = () => {
if (pickUpDate.value) return moment(pickUpDate.value).add(1, 'days');
return moment()
.add(1, 'days')
.format('YYYY/MM/DD');
};
const updateDriverAgeFromPreselectedTraveler = () => {
const driver = preselectTravelers[0];
if (driver.traveler.value.age) {
driverAge.onChange(driver.traveler.value.age);
}
};
const loggedInTravellerConditions = travelerLoggedIn && travelArrangerPresent;
const guestTravellerConditions = !travelerLoggedIn && !cartHasItems && formWithoutChildren;
const showPreselectTravellerButton = () => {
if (
laymanMode &&
(loggedInTravellerConditions || guestTravellerConditions) &&
!props.forceAdvancedSearch
) {
return <PreselectTravelers type="car" />;
}
return null;
};
useEffect(() => {
const vendorCodes = globalPolicyPreferences.carRentalCompanies;
if (vendorCodes.length > 0) {
vendorPreferences.codes.onChange(vendorCodes);
vendorPreferences.type.onChange('exclusive');
}
}, []);
useEffect(() => {
if (!searchForm) {
resetToDefaultSearchParams();
}
}, [searchForm]);
useEffect(() => {
if (!driverAge.touched && preselectTravelers.length > 0) {
updateDriverAgeFromPreselectedTraveler();
}
}, [preselectTravelers]);
return (
<div
className={classNames('grid car-form__container', {
'car-form__container--show': showCarFormContainer,
})}
>
<div className="col-grid direction-row col-12 car-form__specifier-container wrap">
<div className="search-menu__container search-menu__container--location">
<Menu
className="search-menu__container--location-dropdown"
isVisible={isLocationMenuVisible}
size="small"
popOverDirection="bottom-left"
onOutsideClick={onLocationMenuOutsideClick}
label={
<div className="search-menu__menu-label">
{airportSearch.value
? I18n.t('components.ibe.search_form.car.label.search_by.airport')
: I18n.t('components.ibe.search_form.car.label.search_by.location')}
<div
className="search-menu__menu-label--icon"
onClick={() => setIsLocationMenuVisible(prevState => !prevState)}
role="button"
tabIndex={0}
onKeyPress={() => setIsLocationMenuVisible(prevState => !prevState)}
>
<Icon name="iconDownChevron" size="tiny" />
</div>
</div>
}
>
<MenuItem
onClick={() => {
airportSearch.onChange(false);
setIsLocationMenuVisible(false);
}}
>
{I18n.t('components.ibe.search_form.car.label.search_by.location')}
</MenuItem>
<MenuItem
onClick={() => {
airportSearch.onChange(true);
setIsLocationMenuVisible(false);
}}
>
{I18n.t('components.ibe.search_form.car.label.search_by.airport')}
</MenuItem>
</Menu>
</div>
<div className="search-menu__container search-menu__container--vendors">
<Menu
className="search-menu__container--vendors-dropdown"
isVisible={isVendorMenuVisible}
size="small"
popOverDirection="bottom-center"
onOutsideClick={onVendorMenuOutsideClick}
label={
<div className="search-menu__menu-label">
{I18n.t('components.ibe.search_form.car.label.vendor_preferences')}
<div
className="search-menu__menu-label--icon"
onClick={() => setIsVendorMenuVisible(prevState => !prevState)}
role="button"
tabIndex={0}
onKeyPress={() => setIsVendorMenuVisible(prevState => !prevState)}
>
<Icon name="iconDownChevron" size="tiny" />
</div>
</div>
}
>
<div className="search-menu-item__container">
<div className="search-menu-item__specifier">
<div className="col-12 col-bleed">
<div className="search-menu-item__specifier--add-padding-bottom">
<VendorSelectBox
value={vendorPreferences.codes.value}
onChange={value => vendorPreferences.codes.onChange(value || [])}
errorMsg={vendorPreferences.codes.error && vendorPreferences.codes.error[0]}
isMulti={true}
width="full"
size="tiny"
placeholderText={I18n.t('components.ibe.search_form.car.label.choose_vendor')}
isDisabled={disableVendorPreference}
/>
</div>
<SelectBox
width="full"
size="tiny"
value={filterTypeOptions.find(
filter => filter.value === vendorPreferences.type.value,
)}
isDisabled={
disableVendorPreference || vendorPreferences.codes.value.length <= 0
}
options={filterTypeOptions}
onChange={selectedOption =>
vendorPreferences.type.onChange(selectedOption ? selectedOption.value : '')
}
placeholderText={I18n.t('components.ibe.search_form.car.label.filter_type')}
isClearable={false}
/>
</div>
</div>
</div>
</Menu>
</div>
<div className="search-menu__container search-menu__container--drivers-age">
<Menu
className="search-menu__container--drivers-age-dropdown"
isVisible={isAgeMenuVisible}
size="small"
popOverDirection="bottom-center"
onOutsideClick={onAgeMenuOutsideClick}
label={
<div
className={classNames('search-menu__menu-label', {
'search-menu__label--error': driverAge.touched && !driverAge.valid,
})}
>
{I18n.t('components.ibe.search_form.car.label.driver_age')}
<div
className="search-menu__menu-label--icon"
onClick={() => setIsAgeMenuVisible(prevState => !prevState)}
role="button"
tabIndex={0}
onKeyPress={() => setIsAgeMenuVisible(prevState => !prevState)}
>
<Icon name="iconDownChevron" size="tiny" />
</div>
</div>
}
>
<div className="search-menu-item__container">
<div className="search-menu-item__specifier">
<div className="col-12 col-bleed">
<Input
placeholder={I18n.t('components.ibe.search_form.car.label.driver_age')}
size="tiny"
type="number"
{...driverAge}
disabled={cartHasCar}
/>
</div>
</div>
<Alert
className="col-12 col-bleed car-form__error"
hideClose={true}
isLocationMenuVisible={cartHasCar}
type="warning"
>
{I18n.t('components.ibe.search_form.error.change_number_of_travelers')}
</Alert>
</div>
</Menu>
</div>
</div>
<div className="col-12 col-bleed-y">
{airportSearch.value ? (
<Fragment>
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-right">
<AirportAutoComplete
title={I18n.t('components.ibe.search_form.car.label.pick_up.airport')}
error={pickUpLocationAirportCode.touched && pickUpLocationAirportCode.error}
currentAirport={pickUpLocationAirportCode.value}
{...pickUpLocationAirportCode}
onChange={onPickUpAirportChange}
fullWidth={true}
size="tiny"
/>
</div>
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-left">
<AirportAutoComplete
title={I18n.t('components.ibe.search_form.car.label.drop_off.airport')}
error={dropOffLocationAirportCode.error}
currentAirport={dropOffLocationAirportCode.value}
{...dropOffLocationAirportCode}
fullWidth={true}
/>
</div>
</Fragment>
) : (
<Fragment>
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-right">
<LocationSearchInput
address={pickUpGeoLocationAddress.value}
placeholder={I18n.t('components.ibe.search_form.car.label.pick_up.location')}
fullWidth={true}
onAddressChange={pickUpGeoLocationAddress.onChange}
onLatitudeChange={pickUpGeoLocationLatitude.onChange}
onLongitudeChange={pickUpGeoLocationLongitude.onChange}
onSelect={onPickUpLocationSelect}
size="small"
touched={pickUpGeoLocationAddress.touched}
error={pickUpGeoLocationAddress.error}
/>
</div>
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-left">
<LocationSearchInput
address={dropOffGeoLocationAddress.value}
placeholder={I18n.t('components.ibe.search_form.car.label.drop_off.location')}
fullWidth={true}
onAddressChange={dropOffGeoLocationAddress.onChange}
onLatitudeChange={dropOffGeoLocationLatitude.onChange}
onLongitudeChange={dropOffGeoLocationLongitude.onChange}
size="small"
touched={dropOffGeoLocationAddress.touched}
error={dropOffGeoLocationAddress.error}
/>
</div>
</Fragment>
)}
</div>
<div className="col-12">
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-right">
<DatePickerWithFocusedStateWrapper
pastYearsCount={0}
futureYearsCount={1}
size="small"
hideMonthsAndYearsWithNoActiveDates={true}
id={Math.random()}
placeholder={I18n.t('components.ibe.search_form.car.label.pick_up.date')}
onChange={onPickUpDateChange}
fullWidth={true}
error={showInvalidDateError(pickUpDate)}
minDate={moment()
.add(1, 'days')
.format('YYYY/MM/DD')}
date={pickUpDate.value}
locale={moment().locale()}
showClearDate={true}
hideNavButtons={false}
/>
</div>
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-left">
<TimeSelectBox
placeholderText={I18n.t('components.ibe.search_form.car.label.pick_up.time')}
width="full"
onChange={selectedOption => selectedOption && pickUpTime.onChange(selectedOption.value)}
errorMsg={pickUpTime.touched && pickUpTime.error}
value={findTimeSelectBoxValue(pickUpTime.value)}
isClearable={false}
size="small"
/>
</div>
</div>
<div className="col-12 col-bleed-y">
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-right">
<DatePickerWithFocusedStateWrapper
pastYearsCount={0}
futureYearsCount={1}
size="small"
hideMonthsAndYearsWithNoActiveDates={true}
id={Math.random()}
placeholder={I18n.t('components.ibe.search_form.car.label.drop_off.date')}
onChange={onDropOffDateChange}
fullWidth={true}
error={showInvalidDateError(dropOffDate)}
minDate={minDropOffDate()}
date={dropOffDate.value}
locale={moment().locale()}
showClearDate={true}
hideNavButtons={false}
/>
</div>
<div className="col-12 col-sm-6 col-bleed car-form__field--add-padding-left">
<TimeSelectBox
placeholderText={I18n.t('components.ibe.search_form.car.label.drop_off.time')}
width="full"
onChange={selectedOption =>
selectedOption && dropOffTime.onChange(selectedOption.value)
}
errorMsg={dropOffTime.touched && dropOffTime.error}
value={findTimeSelectBoxValue(dropOffTime.value)}
isClearable={false}
size="small"
/>
</div>
</div>
{!props.laymanMode && (
<div className="col-12 car-form__search-accounts">
<div className="accounts">
<SearchAccounts
searchAccounts={props.searchAccounts.cars}
selectedAccounts={accountSettings}
disabled={false}
selectAll={props.selectAll}
toggleSelectAll={props.toggleSelectAll}
selectGds={props.selectGds}
toggleSelectGds={props.toggleSelectGds}
selectDirect={props.selectDirect}
toggleSelectDirect={props.toggleSelectDirect}
/>
</div>
</div>
)}
<Alert
className="col-12 col-bleed hotel-form__error"
hideClose={true}
isLocationMenuVisible={isNoChannelSelected}
type="danger"
>
{I18n.t('components.ibe.search_form.error.no_channel_selected')}
</Alert>
<div className="col-grid direction-row align-center justify-space-between hotel-form__action-wrapper">
<div className="car-form__preselect-traveler">{showPreselectTravellerButton()}</div>
<div className="car-form__action">
<div className="hotel-form__field--add-padding-right">
<Button
label={I18n.t('components.ibe.search_form.label.reset')}
size="normal"
version="v2"
onClick={() => {
resetApp();
resetBookingAppContext();
}}
/>
</div>
<div className="hotel-form__field--add-padding-left">
<Button
label={I18n.t('components.ibe.search_form.label.search')}
size="normal"
version="v2"
disabled={!!submitting || isNoChannelSelected}
onClick={handleSubmit(handleFormSubmission)}
type="primary"
/>
</div>
</div>
</div>
</div>
);
};
export default SearchForm;
the corresponding reducer is
import { combineReducers } from 'redux';
import defaultCarSearchParams from './defaultCarSearchParams';
import searchFilterParamsBySearchId from './searchFilterParamsBySearchId';
import searchParamsBySearchId from './searchParamsBySearchId';
import searchResultsBySearchId from './searchResultsBySearchId';
import searchStatsBySearchId from './searchStatsBySearchId';
import totalResultsBySearchId from './totalResultsBySearchId';
import vendorsByCode from './vendorsByCode';
import totalResultsPerSupplier from './totalResultsPerSupplier'
const carsReducer = combineReducers({
defaultCarSearchParams,
searchFilterParamsBySearchId,
searchParamsBySearchId,
searchResultsBySearchId,
searchStatsBySearchId,
totalResultsBySearchId,
vendorsByCode,
totalResultsPerSupplier,
});
export default carsReducer;
Now I am not sure how to make migration among these from redux-form to react-final-form using my Redux codes.
Goal:
Retrieve data 'smartphones', by API, and apply it as a default selection in the input radio component for the react hook form.
Problem:
The code do not work that makes smartphones as a preselection in the input radio button.
However, if I make a change by using hard coding, it works but hard coding do not solve the case.
I don't know how to solve it.
Info:
*Using React TS and React hook form.
*Newbie in react TS and hook form.
Stackblitz:
https://stackblitz.com/edit/react-ts-z9cnzl
Thank you!
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import './style.css';
type FormValues = {
lastName: string;
favShow: string;
};
export default function App() {
const [category, setCategory] = useState('');
React.useEffect(() => {
async function FetchData() {
var data = await fetch('https://dummyjson.com/products/1').then((res) => {
return res.json();
});
console.log(data.category);
setCategory(data.category);
}
FetchData();
}, []);
const [data, setData] = useState(null);
const { register, handleSubmit } = useForm<FormValues>({
defaultValues: {
lastName: 'asaaaaaaf',
favShow: category,
//favShow: 'smartphones',
},
});
const onSubmit = (data) => {
setData(data);
console.log('asdf');
};
return (
<React.Fragment>
<form onSubmit={handleSubmit(onSubmit)} className="form-container">
<h1 className="form-heading">React Hook Form Example</h1>
<input
{...register('lastName', { required: true })}
className="form-control"
type="text"
placeholder="Last name"
maxLength={15}
name="lastName"
/>
<br />
<br />
<label htmlFor="ted-lasso">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="smartphones"
id="smartphones"
/>{' '}
smartphones
</label>
<label htmlFor="got">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="GOT"
id="got"
/>
GOT
</label>
<br />
<br />
<button className="submit-btn" type="submit">
Submit
</button>
</form>
{data && <p className="submit-result">{JSON.stringify(data)}</p>}
</React.Fragment>
);
}
I got some help and the solution is:
Stackblitz:
https://stackblitz.com/edit/react-ts-m2s6ev?file=index.tsx
import React, { useState } from 'react';
import { useForm } from 'react-hook-form';
import './style.css';
type FormValues = {
lastName: string;
favShow: string;
};
export default function App() {
const [category2, setCategory2] = useState();
const [data, setData] = useState(null);
const { register, handleSubmit, resetField } = useForm<FormValues>({
defaultValues: {
lastName: '',
favShow: '',
},
});
React.useEffect(() => {
async function FetchData() {
var data = await fetch('https://dummyjson.com/products/1').then((res) => {
return res.json();
});
setCategory2(data);
}
FetchData();
}, []);
React.useEffect(() => {
if (category2) {
const obj = JSON.parse(JSON.stringify(category2));
console.log(obj);
resetField('lastName', { defaultValue: obj.discountPercentage });
resetField('favShow', { defaultValue: obj.category });
}
}, [resetField, category2]);
const onSubmit = (data) => {
setData(data);
};
return (
<React.Fragment>
<form onSubmit={handleSubmit(onSubmit)} className="form-container">
<h1 className="form-heading">React Hook Form Example</h1>
<input
{...register('lastName', { required: true })}
className="form-control"
type="text"
placeholder="Last name"
maxLength={15}
name="lastName"
/>
<br />
<br />
<label htmlFor="ted-lasso">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="smartphones"
id="smartphones"
/>{' '}
smartphones
</label>
<label htmlFor="got">
<input
{...register('favShow', { required: true })}
type="radio"
name="favShow"
value="GOT"
id="got"
/>
GOT
</label>
<br />
<br />
<button className="submit-btn" type="submit">
Submit
</button>
</form>
{data && <p className="submit-result">{JSON.stringify(data)}</p>}
</React.Fragment>
);
}
I need some help understanding what and why this does not work when in React JSX, but does in standard JS.
First off, what is it called when you reference a var as a function?
var promiseResolve;
promiseResolve();
And second why does React return: "Uncaught TypeError: promiseResolve is not a function" when trying to call it?
I know this sounds like a intro question, but I've been doing pretty complex JS and now React web builds for five years now, and I embarrassingly don't know what it means to call an undefined function like that.
This all fits into a bigger question of trying to resolve a promise from an await in an external function via a modal, but without trying to go into detail, if the below would work in JSX, it would solve my problems.
var promiseResolve, promiseReject;
var promise = new Promise(function(resolve, reject){
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
For more context, I'm trying to get this to work (EDIT: request to show full code after this blurb. Everything else works, it's only recently I was asked to add a process to warn if an email was already sent):
let promiseResolve, promiseReject;
const resendAttempt = () => new Promise((resolve,reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
const resolvePromise = () => {
promiseResolve();
}
return (
<Modal... <button onclick={resolvePromise} ... /> ... />
<Form onSubmit={async (values ...
await resendAttempt();
)}
/>
)
FULL CODE, look for comments on where issue is:
import React, { useState } from 'react';
import { Formik, Form, useField, FieldArray, Field } from 'formik';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Button from 'components/Elements/Button';
import H3 from 'components/Elements/H2';
import Modal from 'react-bootstrap/Modal';
import BootstrapButton from 'react-bootstrap/Button';
import Wrapper from './Wrapper';
const TeamRecruitmentForm = props => {
const [showForm, setShowForm] = useState(true);
const [customFormError, setCustomFormError] = useState(false);
const [customFormSuccess, setCustomFormSuccess] = useState(false);
const [recruitRow, setRecruitRow] = useState({
recruitFirstName: '',
recruitLastName: '',
recruitEmail: ''
});
const captFirstName = props.userData.name ? props.userData.name.first : '';
const captLastName = props.userData.name ? props.userData.name.last : '';
const cityName = props.customGreetingFields.eventCity ? props.customGreetingFields.eventCity : '';
let messageSubject = `Join ${captFirstName} ${captLastName}\'s Leaders for Life Team`;
let promiseResolve, promiseReject;
const resendAttempt = () => new Promise((resolve,reject) => {
promiseResolve = resolve;
promiseReject = reject;
});
// this is called with a modaal button click
const resolvePromise = () => {
promiseResolve(); // JS errror, console says it is not a function
}
const [showTeammember, setShowTeammember] = useState(false);
const [showResend, setShowResend] = useState(false);
const handleClose = () => {
console.log('handleClose');
setShowTeammember(false);
setShowResend(false);
};
const handleShow = (modal) => {
console.log('handleShow');
if (modal == 'teammember') {
setShowTeammember(true);
} else if (modal == 'resend') {
setShowResend(true);
}
};
function validateEmail(value,x) {
let error;
const filterTeamRoster = props.teamRosterActivity.filter(user => {
return user.email == value;
});
if (!value) {
error = 'Required';
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) {
error = 'Invalid email address';
}
else if (filterTeamRoster.length > 0) {
handleShow('teammember');
error = 'Current Teammember';
}
return error;
}
function validateFirstName(value) {
let error;
if (!value) {
error = 'Required';
}
return error;
}
function validateLastName(value) {
let error;
if (!value) {
error = 'Required';
}
return error;
}
return (
<>
<Modal
show={showTeammember}
id={props.id}
size={props.size}
onHide={handleClose}
className={`mx-auto ${props.modalClass}`}
centered
>
<Modal.Header closeButton closeLabel="Back" className="bg-primary py-1">
<BootstrapButton
variant="link"
onClick={handleClose}
className={`text-white btn-modal-back btn-back-${props.modalClass}`}
>
<span className="fas fa-caret-left" alt="" />
Back
</BootstrapButton>
</Modal.Header>
<Modal.Body>
<H3>Current Teammember</H3>
<p>The person associated with the email address has already joined your team</p>
<Button variant="primary" className="btn-block" onClick={handleClose}>
OK
</Button>
</Modal.Body>
</Modal>
<Modal
show={showResend}
id={props.id}
size={props.size}
onHide={handleClose}
className={`mx-auto ${props.modalClass}`}
centered
>
<Modal.Header closeButton closeLabel="Back" className="bg-primary py-1">
<BootstrapButton
variant="link"
onClick={handleClose}
className={`text-white btn-modal-back btn-back-${props.modalClass}`}
>
<span className="fas fa-caret-left" alt="" />
Back
</BootstrapButton>
</Modal.Header>
<Modal.Body>
<H3>You already sent an invite to this email address.</H3>
<p>Would you like to send it again"</p>
{/* This is the button I'm using to test allowing the dupe email
form to be send */}
<Button variant="primary" className="btn-block" onClick={resolvePromise}>
OK
</Button>
</Modal.Body>
</Modal>
<Wrapper key={props.formNumber} {...props}>
{customFormError === false && customFormSuccess === true ? (
<div className="my-1">
<Row>
<Col xs={6} md={3}
className="pr-1 pr-md-2 pb-2 email-sent-container"><span className="email-sent">{recruitRow.recruitFirstName}</span></Col>
<Col xs={6} md={3}
className="pr-1 pr-md-2 pb-2 email-sent-container"><span className="email-sent">{recruitRow.recruitLastName}</span></Col>
<Col md={4}
className="pl-md-2 pb-2 email-sent-container"><span className="email-sent">{recruitRow.recruitEmail}</span></Col>
<Col xs={6} md={2}
className="pb-2">
<Button variant="primary" type="submit" disabled>
Sent!
</Button>
</Col>
</Row>
</div>
) :
(<Formik
{...props}
initialValues={{ recruits: [{firstName: "", lastName: "", emailAddress: ""}] }}
validateOnChange={false}
validateOnBlur={false}
onSubmit={ async (values, { setSubmitting, setFieldError, setStatus }) => {
// this is the array it checks in order to kick bcak option to continue
// or cancel the submissions process
const filterSentMessages = props.sentMessages.filter(user => {
return user.recipients == values['emailAddress'+props.formNumber];
});
console.log("before sleep");
if (filterSentMessages.length > 0) {
handleShow('resend');
}
// this works, but the button click in the modal
// to resolve this is the issue where I get undefined
await resendAttempt();
let recipient = `${values['firstName'+props.formNumber]} ${values['lastName'+props.formNumber]} <${encodeURIComponent(values['emailAddress'+props.formNumber])}>,`;
console.info('recipient:', recipient);
let messageBody = `<p>Dear ${values['firstName'+props.formNumber]},</p><p>... message content ...</p><p>Thank you in advance for your commitment to making an impact!</p><p>${captFirstName}!</p>`
messageBody = encodeURIComponent(messageBody);
let x = 2; // this is here to just stop sending so many test emails
if( x == 1 ) { // this is here to just stop sending so many test emails
try {
props.sendMessage(
localStorage.getItem('frId'),
messageSubject,
messageBody,
recipient,
props.eventConfig.defaultStationeryId,
);
} catch (error) {
console.log('form submit error:', error);
setShowForm(!showForm);
setSubmitting(false);
}
}
setRecruitRow({...recruitRow, recruitFirstName: values['firstName'+props.formNumber], recruitLastName: values['lastName'+props.formNumber], recruitEmail: values['emailAddress'+props.formNumber]})
setCustomFormError(false);
setCustomFormSuccess(true);
setSubmitting(false);
}}
render={({ values,errors }) => (
<Form className="my-1" id={`recruitForm${props.formNumber}`}>
<FieldArray
name="recruits"
render={(arrayHelpers) => (
<>
{(values.recruits && values.recruits.length > 0) && (
values.recruits.map((recruit, index) => (
<Row key={props.formNumber}>
<Col xs={6} md={3}
className="pr-1 pr-md-2 pb-2"
>
<label htmlFor={`firstName${props.formNumber}`} className="sr-only">
First Name
</label>
<Field
className="form-control"
type="text"
name={`firstName${props.formNumber}`}
placeholder="First Name"
id={`firstName${props.formNumber}`}
validate={validateFirstName}
/>
{errors['firstName'+props.formNumber] && <div className="text-danger" role="alert">{errors['firstName'+props.formNumber]}</div>}
</Col>
<Col xs={6} md={3}
className="pl-1 px-md-2 pb-2"
>
<label htmlFor={`lastName${props.formNumber}`} className="sr-only">
Last Name
</label>
<Field
className="form-control"
type="text"
name={`lastName${props.formNumber}`}
placeholder="Last Name"
id={`lastName${props.formNumber}`}
validate={validateLastName}
/>
{errors['lastName'+props.formNumber] && <div className="text-danger" role="alert">{errors['lastName'+props.formNumber]}</div>}
</Col>
<Col md={4}
className="pl-md-2 pb-2"
>
<label htmlFor={`emailAddress${props.formNumber}`} className="sr-only">
Email Address
</label>
<Field
className="form-control"
type="text"
name={`emailAddress${props.formNumber}`}
placeholder="Email Address"
id={`emailAddress${props.formNumber}`}
validate={validateEmail}
/>
{errors['emailAddress'+props.formNumber] && <div className="text-danger" role="alert">{errors['emailAddress'+props.formNumber]}</div>}
</Col>
<Col xs={6} md={2}
className="pb-2"
>
<Button variant="primary" type="submit">
Send
</Button>
</Col>
</Row>
))
)}
</>
)}
/>
</Form>
)}
/>
)}
</Wrapper>
</>
);
};
export default TeamRecruitmentForm;
I am implementing a component that handles an redux action to add comments but it is not working.No error is generated
I have tried calling the props from other regions in the code but that doesnt seem to work.The addComment Action should add the comments rendered in the DishDetails comments section.However no additions are made.
ActionTypes.js
export const ADD_COMMENT='ADD_COMMENT';
ActionCreators.js
import * as ActionTypes from './ActionTypes';
export const addComment=(dishId,rating, author, comment)=>({
type: ActionTypes.ADD_COMMENT,
payload: {
dishId:dishId,
rating:rating,
author:author,
comment:comment
}
});
comments.js
import { COMMENTS } from '../shared/comments';
import * as ActionTypes from './ActionTypes';
export const Comments= (state= COMMENTS, action) => {
switch(action.type){
case ActionTypes.ADD_COMMENT:
var comment= action.payload;
comment.id= state.length;
comment.date = new Date().toISOString();
return state.concat(comment);
default:
return state;
}
};
MainComponent.js
import React, { Component } from 'react';
import Header from './HeaderComponent';
import Footer from './FooterComponent';
import Menu from './MenuComponent';
import DishDetail from './DishDetail';
import Home from './HomeComponent';
import { Switch, Route, Redirect, withRouter } from 'react-router-dom';
import Contact from './ContactComponent';
import About from './AboutComponent';
import { connect } from 'react-redux';
import {addComment} from '../redux/ActionCreators';
const mapStateToProps = state =>{
return{
dishes: state.dishes,
comments: state.comments,
promotions: state.promotions,
leaders: state.leaders
}
};
const mapDispatchToProps = dispatch => ({
addComment: (dishId,rating, author, comment)=>dispatch(addComment(dishId,rating, author, comment))
});
class Main extends Component {
constructor(props) {
super(props);
}
render() {
const HomePage= ()=>{
return(
<Home dish={this.props.dishes.filter((dish)=>dish.featured)[0]}
promotion={this.props.promotions.filter((promotion)=>promotion.featured)[0]}
leader={this.props.leaders.filter((leader)=>leader.featured)[0]}
/>
);
}
const DishWithId = ({match})=>{
return(
<DishDetail dish={this.props.dishes.filter((dish)=>dish.id === parseInt(match.params.dishId,10))[0]}
comments={this.props.comments.filter((comment)=>comment.dishId=== parseInt(match.params.dishId,10))}
addComment={this.props.addComment}/>
);
}
const AboutPage = ()=>{
return(
<About leaders={this.props.leaders}/>
);
}
return (
<div>
<Header/>
<Switch>
<Route path="/home" component={HomePage} />
<Route exact path="/menu" component={()=><Menu dishes ={this.props.dishes} />} />
<Route path="/menu/:dishId" component={DishWithId}/>
<Route exact path="/aboutus" component={() => <AboutPage leaders={this.props.leaders} />} />}/>
<Route exact path="/contactus" component={Contact}/>
<Redirect to="/home"/>
</Switch>
<Footer/>
</div>
);
}
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Main));
DishDetail.js
import React, { Component } from 'react';
import { Card, CardImg, CardImgOverlay, CardText, CardBody, CardTitle, Breadcrumb, BreadcrumbItem , Button, Modal, ModalHeader,ModalBody, Form, FormGroup, Input, Label, Col, Row } from 'reactstrap';
import {Control, LocalForm, Errors} from 'react-redux-form';
import {Link} from 'react-router-dom';
const required = (val) =>val && val.length;
const maxLength = (len) => (val) => !(val) || (val.length <= len);
const minLength = (len) => (val) => val && (val.length >= len);
class DishDetail extends Component{
constructor(props){
super(props);
this.state={
dish:props.dish,
isCommentModalOpen: false,
};
this.toggleCommentModal=this.toggleCommentModal.bind(this);
}
toggleCommentModal(){
this.setState({
isCommentModalOpen:!this.state.isCommentModalOpen
});
}
handleSubmit(props,values){
alert("State" + JSON.stringify(props.addComment(props.dishId, values.rating, values.author, values.comment)));
// this.state.addComment(this.state.dishId, values.rating, values.author, values.comment)
}
render(){
const RenderDish=({dish})=>{
return(
<Card>
<CardImg top src={dish.image} alt={dish.name}/>
<CardBody>
<CardTitle>{dish.name}</CardTitle>
<CardText>{dish.description}</CardText>
</CardBody>
</Card>
);
}
const RenderComments=({comments})=>{
const comment_layout= comments.map((comment)=>{
if(comment.comment!=null){
return(
<div>
{comment.comment}
{comment.author}, {new Intl.DateTimeFormat('en-US',{year:'numeric',month:'short',day:'2-digit'}).format(new Date(Date.parse(comment.date)))}
</div>
);
}else{
return(
<div></div>
);
}
});
return(comment_layout);
}
const CommentForm=()=>{
return(
<Button outline onClick={this.toggleCommentModal}>
<span className="fa fa-edit fa-lg">Submit Comment</span>
</Button>
);
}
if (this.state.dish!==undefined){
return (
<div className="container">
<div className="row">
<Breadcrumb>
<BreadcrumbItem>
<Link to="/menu">Menu</Link>
</BreadcrumbItem>
<BreadcrumbItem active>{this.state.dish.name}
</BreadcrumbItem>
</Breadcrumb>
<div className="col-12">
<h3>{this.state.dish.name}</h3>
<hr/>
</div>
</div>
<div className="row ">
<div className="col-12 col-md-5 m-1">
<RenderDish dish={this.state.dish}/>
</div>
<div className="col-md-5 col-sm-12 m-1">
<h4>Comment</h4>
<RenderComments comments={this.props.comments}
/>
<CommentForm/>
</div>
</div>
<Modal isOpen={this.state.isCommentModalOpen} toggle={this.toggleCommentModal}>
<ModalHeader toggle={this.toggleCommentModal}>
Submit Comment </ModalHeader>
<ModalBody>
<div className="col-12">
<LocalForm onSubmit={(values)=>this.handleSubmit(this.props,values)}>
<Row className="form-group">
<Label htmlFor ="rating" md={2}>Rating</Label>
<Control.select model=".rating" id="rating" name="rating" className="form-control">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
</Control.select>
</Row>
<Row className="form-group">
<Label htmlFor ="name" md={2}>Name</Label>
<Control.text model=".name" className="form-control" id="name" name="name" placeholder="name" validators={{required,minLength: minLength(3),maxLength:maxLength(15)}} />
<Errors className="text-danger"
model=".name"
show="touched"
messages={{
required:'Required',
minLength:'Must be greater than 2 char',
maxLength: 'Must be 15 chars or less'
}}
/>
</Row>
<Row className="form-group">
<Label htmlFor ="feedback" md={2}>Comment</Label>
<Control.textarea model=".message" className="form-control" id="message" name="message" rows="12" />
</Row>
<Row className="form-group">
<Button type="submit" color="primary">
Submit
</Button>
</Row>
</LocalForm>
</div>
</ModalBody>
</Modal>
</div>
);
}else{
return(
<div></div>
);
}
}
}
export default DishDetail;
you are not dispatching to the reducer in the action. Do it like this
export const addComment = (dishId, rating, author, comment) => {
return (dispatch) => {
dispatch({
type: ActionTypes.ADD_COMMENT,
payload: {
dishId: dishId,
rating: rating,
author: author,
comment: comment
}
})
}
};
I am trying to integrate react-bootstrap Dropdown component in redux form. This is my code
import React from 'react'
import {Input,Form,FormGroup,FormControl,ControlLabel,Button,ButtonToolbar,Panel,Checkbox,Radio} from 'react-bootstrap'
import { reduxForm,Field,reset } from 'redux-form'
const FieldInput = ({ input, meta, type, placeholder, min, max }) => {
return (
<FormControl
type={type} onChange={input.onChange} />
)
}
class dropDown extends React.Component {
render() {
return (<FormControl componentClass="select" {...this.props} {...this.input} />)
}
}
let Gri = props => {
const { handleSubmit,pristine,submitting} = props
return (
<form onSubmit={handleSubmit}>
<div>
<FormGroup controlId="formInlineName">
<ControlLabel>User Name:</ControlLabel>
<Field name="firstname" component={FieldInput} type="text"/>
</FormGroup>
</div>
<div>
<FormGroup controlId="formControlsSelect">
<ControlLabel>Category:</ControlLabel>
<Field name="category_id" component={dropDown}>
<option value="1">Health</option>
<option value="2">Municipality Roads</option>
<option value="3">Women and Child Development</option>
</Field>
</FormGroup>
</div>
<Button bsStyle="success" type="submit" disabled={submitting}>SUBMIT</Button>
</form>
)
}
const onSubmit = values => {
console.log(values)
}
Gri= reduxForm({
form: 'gri',
onSubmit,
})(Gri)
export default Gri
In the output the dropdown list is shown but the value is not returned when I click submit. Please help me finding the solution