React multiselection with Formik not reading the selected value - reactjs

I'm using Formik and windmill for the multiselect form, see picture below. The problem is that I implemented a search functionality to look into the array of options and now it appears that even though I'm selecting an option it is not passing the value to Formik and is not submitting the form. Any help would be greatly appreciated. Thanks!
form
import { React, useState } from 'react';
import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';
import { partService } from '../../services';
import { SearchIcon, AddIcon } from '../../icons';
import { Input, Label, Select, HelperText } from '#windmill/react-ui';
function CreateElementForm({ formRef, callback, partId }) {
const [searchTerm, setSearchTerm] = useState('');
const [iconSelection, setIconSelection] = useState(true);
const tempArray = [
{ name: 'data1', value: 'value1' },
{ name: 'data2', value: 'value2' },
{ name: 'data3', value: 'value3' },
{ name: 'data4', value: 'value4' },
];
return (
<Formik
innerRef={formRef}
initialValues={{
description: '',
percentage: '',
material: '',
}}
validationSchema={Yup.object().shape({
description: Yup.string().required('Element name is required'),
percentage: Yup.number().required('Percentage is required'),
material: Yup.string().required('Material is required'),
})}
onSubmit={(
{ description, percentage, material },
{ setStatus, setSubmitting }
) => {
setStatus();
setTimeout(async () => {
await partService
.createElement(partId, description, percentage, material)
.then(
(response) => {
callback(true);
},
(error) => {
if (error.response) {
setStatus(error.response.data.message);
} else {
setStatus('Some error occured.');
}
callback(false);
}
);
}, 400);
}}
>
{({ errors, status, touched, isSubmitting, setFieldValue, values }) => (
<Form>
<Label>
<span>Description</span>
<Field
className='mt-1'
as={Input}
name='description'
type='text'
placeholder='Part Element'
/>
<ErrorMessage name='name'>
{(msg) => <HelperText valid={false}>{msg}</HelperText>}
</ErrorMessage>
</Label>
<Label>
<span>Percentage</span>
<Field
className='mt-1'
as={Input}
name='percentage'
type='number'
placeholder='%'
/>
<ErrorMessage name='percentage'>
{(msg) => <HelperText valid={false}>{msg}</HelperText>}
</ErrorMessage>
</Label>
<Label>
<span>Search Field</span>
<div className='relative text-gray-500 focus-within:text-purple-600 dark:focus-within:text-purple-400'>
<Input
className='mt-1 pl-10 text-black dark:text-gray-300'
placeholder='Jane Doe'
onChange={(event) => {
setSearchTerm(event.target.value);
}}
/>
<div className='absolute inset-y-0 flex items-center ml-3 pointer-events-none'>
{iconSelection ? (
<SearchIcon className='w-5 h-5' aria-hidden='true' />
) : (
<AddIcon className='w-5 h-5' aria-hidden='true' />
)}
</div>
</div>
</Label>
<Label className='mt-4'>
<span>Multiselect</span>
<Field
className='mt-1'
as={Select}
name='material'
multiple={true}
type='text'
onChange={(evt) =>
setFieldValue('material', evt.target.selectedOptions.val)
}
>
{tempArray
.filter((value) => {
if (searchTerm === '') {
return value;
} else if (
value.name.toLowerCase().includes(searchTerm.toLowerCase())
) {
return value;
}
})
.map((i) => (
<option key={i.name} val={i.value}>
{i.name}
</option>
))}
</Field>
</Label>
{status && <HelperText valid={false}>{status}</HelperText>}
</Form>
)}
</Formik>
);
}
export default CreateElementForm;

Related

Select dropdown triggers re-validations of already filled input fields using formik and yup

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.

Why am i POST a lot of unwanted data?

I'm kind of new with java and ReactJS and I have a big issue where when I tried to post my data, it posted a lot of unwanted data like rendering the whole table.
My console.log() printed this :
location_dest_id: 2
location_id: 1
origin: "test3"
picking_type_id: 1
stock_move_ids: Array(1)
0:
altKey: false
bubbles: true
button: 0
buttons: 0
cancelable: true
clientX: 317
clientY: 652
ctrlKey: false
currentTarget: null
defaultPrevented: false
demand: "12"
detail: 1
done: "0"
eventPhase: 3
getModifierState: ƒ modifierStateGetter(keyArg)
isDefaultPrevented: ƒ functionThatReturnsFalse()
isPropagationStopped: ƒ functionThatReturnsFalse()
isTrusted: true
metaKey: false
movementX: 0
movementY: 0
nativeEvent: PointerEvent {isTrusted: true, pointerId: 1, width: 1,
height: 1, pressure: 0, …}
pageX: 317
pageY: 754
product_tmpl_id: 9
product_uom: "1"
relatedTarget: null
screenX: 317
screenY: 723
shiftKey: false
target: span
timeStamp: 203547.59999990463
type: "click"
view: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
_reactName: "onClick"
_targetInst: null
From what I understand, the error is when I tried to pass "stock_move_ids" nested data, it POST so much data as like the console.log() above. What it should be passed is like this :
[
{
"date":"02-09-2022",
"origin":"test2",
"picking_type_id":2,
"location_id":1,
"location_dest_id":2,
"stock_move_ids":
[
{
"demand":12,
"done":0,
"product_uom":1,
"product_tmpl_id":18
}
]
}
]
Is there any way to solve my problem? My code is based on this template : https://codesandbox.io/s/j0opp
Here's my code looks like :
Parent
import React, { useEffect, useContext } from "react";
import { Button, Form, Input, DatePicker, Select } from 'antd';
import { Stockmovetable } from "./Stockmovetable";
import { AppContext } from '../../../context/Appcontext'
const Stockpickingnew = ({ title }) => {
const { Function, State } = useContext(AppContext)
const { fetchDataPickingType, fetchDataLocation, fetchDataPupuk, option, stock_move_ids, StockPick, StockPickFailed } = Function
const { dateFormat, dataPupuk, dataStockLocation, dataStockPickingType } = State
useEffect(() => {
fetchDataPickingType()
fetchDataLocation()
fetchDataPupuk()
}, [])
return (
<>
<div className='new'>
<div className="top">
<h1>{title}</h1>
</div>
<div className="bottom">
<div className="stockPicking">
<Form
name="stockPickings"
layout="vertical"
onFinish={StockPick}
onFinishFailed={StockPickFailed}
autoComplete="off"
>
<div className="left">
<Form.Item
label="Origin :"
name='origin'
>
<Input placeholder="Origin" />
</Form.Item>
<Form.Item
label="Picking Type :"
name='picking_type_id'
>
<Select
placeholder="Picking Type"
options={dataStockPickingType.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
<Form.Item
label="Date :"
name='date'
>
<DatePicker
format={dateFormat}
/>
</Form.Item>
</div>
<div className="right">
<Form.Item
label="Location :"
name='location_id'
>
<Select
placeholder="Tujuan Awal"
options={dataStockLocation.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
<Form.Item
label="Destination :"
name='location_dest_id'
>
<Select
placeholder="Tujuan Akhir"
options={dataStockLocation.map(e => ({label: e.name, value: e.id}))}
/>
</Form.Item>
</div>
<div className="stockMove">
<Form.List name="stock_move_ids">
{(stock_move_ids, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
</div>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
</>
)
}
export default Stockpickingnew
Child
import React from "react";
import { Form, Input, Button, Table, Select } from "antd";
import { PlusOutlined, MinusOutlined } from "#ant-design/icons";
const { Column } = Table;
export const Stockmovetable = props => {
const { stock_move_ids, add, remove, dataPupuk, option } = props;
return (
<Table
dataSource={stock_move_ids}
pagination={false}
footer={() => {
return (
<Form.Item>
<Button onClick={add}>
<PlusOutlined /> Add field
</Button>
</Form.Item>
);
}}
>
<Column
dataIndex={"product_tmpl_id"}
title={"Nama Produk"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "product_tmpl_id"]}>
<Select
placeholder="Produk"
options={dataPupuk.map(e => ({ label: e.name, value: e.id }))}
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"demand"}
title={"Demand"}
render={(value, row, index) => {
// console.log(row);
return (
<Form.Item name={[index, "demand"]}>
<Input
placeholder="Demand"
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"done"}
title={"Done"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "done"]}>
<Select
placeholder="Tujuan Akhir"
options={option}
/>
</Form.Item>
);
}}
/>
<Column
dataIndex={"product_uom"}
title={"product_uom"}
render={(value, row, index) => {
return (
<Form.Item name={[index, "product_uom"]}>
<Input
placeholder="product_uom"
/>
</Form.Item>
);
}}
/>
<Column
title={"Action"}
render={(value, row, index) => {
return (
<React.Fragment>
<Button
icon={<MinusOutlined />}
shape={"circle"}
onClick={() => remove(row.name)}
/>
</React.Fragment>
);
}}
/>
</Table>
);
};
Context
export const AppContext = createContext()
export const AppProvider = props => {
const Navigate = useNavigate()
const dateFormat = ['DD-MM-YYYY'];
const StockPick = (values) => {
console.log('Success:', values);
let stockpick = [{
date: moment(values.date).format("DD-MM-YYYY"),
origin: values.origin,
picking_type_id: values.picking_type_id,
location_id: parseInt(values.location_id),
location_dest_id: parseInt(values.location_dest_id),
stock_move_ids: [
{
demand: parseInt(values?.stock_move_ids?.[0]?.demand),
done: parseInt(values?.stock_move_ids?.[0]?.done),
product_uom: parseInt(values?.stock_move_ids?.[0]?.product_uom),
product_tmpl_id: values?.stock_move_ids?.[0]?.product_tmpl_id,
},
],
}];
let params = JSON.stringify(stockpick)
console.log(params)
axios.post('http://127.0.0.1:5000/api/stockpickings', params, { headers })
.then(() => {
Navigate('/')
})
.catch(error => {
if (error.response) {
console.log(error.response);
}
});
};
}
I think this is all I can provide / needed to fix my codings, if there's anything I need to add and or fix, please tell me. Thank you.
Okay after some hours tries
as the comment above, i think all i need to do is just not making the body as dataindex.
from :
<Form.List name="stock_move_ids">
{(stock_move_ids, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
to
<Form.List name="stock_move_ids">
{(stock_move, { add, remove }) => {
return <Stockmovetable stock_move_ids={stock_move_ids} option={option} add={add} remove={remove} dataPupuk={dataPupuk} />;
}}
</Form.List>
Thank you very much for the helps!

React Formik Material-UI autocomplete: how to submit multiple selected values on submit

I'm trying to figure out how to use Material-UI Autocomplete with Formik.
Here I've created a form which displays 3 question fields per page, which is working fine.
Since I'm very new to this and don't really understand Formik very well I'm having trouble with this type of input.
So what I'm doing here is that first there is a condition, you select (yes or no) depending on if you want to answer the question. After that there is the Autocomplete dropdown, from which you can select multiple options. The part where I'm having trouble is I don't know how to submit multiple selected options. The options you select from the autocomplete dropdown will go into the remedies array.
In the end it should return something like this:
question11: {
agree: 'Yes',
remedies: ['option you selected', 'another selected option']
}
So I want to be able to select multiple values from Autocomplete and populate them in the remedies: [] array.
The FormikControl is another thing I made which is just a bunch of switch statements which render a certain type of Input depending on the condition that is passed. In this case its returning the Input Component I posted below.
The form:
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { Formik, Form } from 'formik';
import * as yup from 'yup';
import FormikControl from './FormikControl';
import SurveyFormLayout from '../surveyForm/SurveyFormLayout';
import PrimaryBtn from '../../../components/buttons/PrimaryBtn';
const data = [
{
type: 'radioWithDropdown',
question: 'Do you take any natural remedies? (Vitamins, minerals, amino acids,
herbs etc.)',
name: 'question11',
conditions: [ { key: 'yes', value: 'Yes' }, { key: 'no', value: 'No' } ],
options: [
{ key: 'Option 11', value: 'word' },
{ key: 'Option 12', value: 'another word' },
{ key: 'Option 13', value: 'some other' },
{ key: 'Option 14', value: 'random' }
]
}
]
const InputAssesment = () => {
const initialValues = {
question11: {
agree: '',
remedies: []
}
};
const validationSchema = yup.object({
question11: yup.object().shape({
agree: yup.string(),
remedies: yup.array()
})
});
return (
<SurveyFormLayout>
<div className="survey-form__container">
<FormikStepper
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={() => {}}
>
<FormikStep>
<FormikControl
control="conditionWithSelectMultiple"
label={data[9].question}
name="question11.remedies"
options={data[9].options}
conditionName="question11.agree"
conditionOptions={data[9].conditions}
/>
</FormikStep>
</FormikStepper>
</div>
</SurveyFormLayout>
)
}
export function FormikStep({ children }) {
return <div className="formik-step">{children}</div>;
}
export function FormikStepper({ children, ...props }) {
const childrenArray = React.Children.toArray(children);
const [ step, setStep ] = useState(0);
const currentChild = childrenArray[step];
function isLastStep() {
return step === childrenArray.length - 1;
}
return (
<Formik
{...props}
onSubmit={async (values, helpers) => {
if (isLastStep()) {
await props.onSubmit(values, helpers);
} else {
setStep((s) => s + 1);
}
console.log(values);
}}
>
<Form autoComplete="off" className="formik-form">
{currentChild}
<div className="survey-btns-container">
{step > 0 ? (
<button
className="btn-secondary survey-back-btn"
type="button"
onClick={() => setStep((s) => s - 1)}
>
Back
</button>
) : null}
<PrimaryBtn text={isLastStep() ? 'Submit' : 'Next'} type="submit" />
</div>
</Form>
</Formik>
);
}
The input component:
import React from 'react';
import { Field } from 'formik';
import Checkbox from '#material-ui/core/Checkbox';
import TextField from '#material-ui/core/TextField';
import { Autocomplete } from 'formik-material-ui-lab';
import CheckBoxOutlineBlankIcon from '#material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '#material-ui/icons/CheckBox';
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
const ConditionWithSelectMultiple = (props) => {
const { label, name, options, conditionName, conditionOptions, ...rest } = props;
return (
<div className="question-field">
<label className="survey-question-h">{label}</label>
<div className="radio-options-container">
<Field name={conditionName} {...rest}>
{({ field }) => {
return conditionOptions.map((option) => {
return (
<div key={option.key} className="radio-button-option">
<input
className="radio-button"
type="radio"
id={option.value}
{...field}
value={option.value}
checked={field.value === option.value}
/>
<div className="radio-button-gap" />
<label htmlFor={option.value} className="radio-button-option-label">
{option.value}
</label>
</div>
);
});
}}
</Field>
</div>
<Field
name="name"
component={Autocomplete}
multiple
options={options}
disableCloseOnSelect
getOptionLabel={(option) => option.key}
renderOption={(option, { selected }) => (
<React.Fragment>
<Checkbox icon={icon} checkedIcon={checkedIcon} style={{ marginRight: 8 }} checked={selected} />
{option.key}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => <TextField {...params} variant="outlined" placeholder="Select" />}
/>
</div>
);
};
export default ConditionWithSelectMultiple;
Any help is more than welcome

React-Hook-Form Validation

I'm looking to implement the form validation using react-hook. However, I'm facing some trouble in doing so as I've also added some stuff on my own under the handleSubmit and I'm not really sure how to go about it.
export default function Contact() {
const [message, setMessage] = useState(false);
const [alert, setAlert] = useState(true);
const { register, errors} = useForm();
const [showElement, setShowElement] = React.useState(false);
const handleSubmit = (e) => {
e.preventDefault();
emailjs.sendForm('', '', e.target, '')
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
e.target.reset();
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};
const onSubmit= data=>{
console.log(data);
}
return (
<div className="right">
<h2>Contact Me</h2>
<form onSubmit={handleSubmit} id="contactform">
<input type="text" placeholder="Name" name="name" ref={register({required: true, minLength: 2})}
required />
<button type="submit">Send</button>
</form>
{showElement ? (
<div className="submitmsg">
{message && (
<span> Messaged received. I'll respond to your query ASAP! </span>
)}
</div>
) : (
<div> </div>
)}{" "}
</div>
)
}
Thank you!
React hook form provides the handeSubmit method that receives the form data after validations. Also, you must use the errors object to show errors in the UI.
Here is the sandbox link: https://codesandbox.io/s/exciting-dust-df5ft?file=/src/App.js
I have updated your code accordingly:
import { useState } from "react";
import React from "react";
import emailjs from "emailjs-com";
import { useForm } from "react-hook-form";
export default function Contact() {
const [message, setMessage] = useState(false);
const {
register,
handleSubmit,
formState: { errors }
} = useForm();
const [showElement, setShowElement] = React.useState(false);
const onSubmit = (data) => {
emailjs
.send(
"service_t1ccrgq",
"template_gmmcyzr",
data,
"user_d0vUwhmqvbIYhEsyZF8tu"
)
.then(
(result) => {
console.log(result.text);
},
(error) => {
console.log(error.text);
}
);
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};
return (
<div className="contact" id="contact">
<div className="left">
<img className="contactme" src="asset/email.gif" />
</div>
<div className="right">
<h2>Contact Me</h2>
<form onSubmit={handleSubmit(onSubmit)} id="contactform">
<input
type="text"
placeholder="Name"
name="name"
{...register("name", {
required: "Name is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<input
type="tel"
placeholder="Mobile Number"
name="mobile"
{...register("mobile", {
required: "Mobile Number is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<input
type="text"
placeholder="Email"
name="email"
{...register("email", {
required: "Email is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
/>
<textarea
placeholder="Message"
required
name="message"
{...register("message", {
required: "Message is Required",
minLength: {
value: 3,
message: "Should be greater than 3 characters"
}
})}
></textarea>
<button type="submit">Send</button>
</form>
{showElement ? (
<div className="submitmsg">
{message && (
<span> Messaged received. I'll respond to your query ASAP! </span>
)}
</div>
) : (
<div> </div>
)}{" "}
</div>
{errors.name && (
<div>
<span>{errors.name.message}</span>
</div>
)}
{errors.message && (
<div>
<span>{errors.message.message}</span>
</div>
)}
{errors.email && (
<div>
<span>{errors.email.message}</span>
</div>
)}
{errors.mobile && (
<div>
<span>{errors.mobile.message}</span>
</div>
)}
</div>
);
}
You have to first initialize handleSubmit as below.
const {handleSubmit} = useForm();
Then in the form, onSubmit should be as below.
<form onSubmit={handleSubmit(onSubmit)}>
"onSubmit" is the method that is used to write the code in submitting form.
Regards.
In your code, it should be as below.
const onSubmit = (e) => {
e.preventDefault();
emailjs.sendForm('', '', e.target, '')
.then((result) => {
console.log(result.text);
}, (error) => {
console.log(error.text);
});
e.target.reset();
setMessage(true);
setShowElement(true);
setTimeout(function () {
setShowElement(false);
}, 4000);
};

React-select with Formik not updating select field but does everything else

React-Select with Formik is not loading the selected value in select componenet but I'm able to get values on form submission and validation also works with Yup
Here is a codesandbox demo for the same - https://codesandbox.io/s/wild-violet-fr9re
https://codesandbox.io/embed/wild-violet-fr9re?fontsize=14
import React, { Component } from "react";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";
const debug = true;
class SelectForm extends Component {
constructor(props) {
super(props);
this.state = {
stateList: [],
stateCity: "",
selectedState: "",
citiesToLoad: []
};
}
handleState(opt) {
console.log(opt.value);
let citiesList = [];
Object.keys(this.state.stateCity).forEach(key => {
if (key === opt.value) {
this.state.stateCity[key].map((cityName, j) => {
citiesList.push(cityName);
});
}
});
this.setState({
selectedState: opt.value,
citiesToLoad: citiesList
});
}
handleMyCity(opt) {
console.log(opt.value);
}
componentDidMount() {
let stateLi = [];
fetch(`stateCity.json`)
.then(response => {
console.log(response);
return response.json();
})
.then(data => {
console.log(data);
for (let key in data) {
if (data.hasOwnProperty(key)) {
stateLi.push(key);
}
}
this.setState({ stateCity: data, stateList: stateLi });
})
.catch(err => {
console.log("Error Reading data " + err); // Do something for error here
});
}
render() {
const { selectedState, stateList, citiesToLoad } = this.state;
const newStateList = stateList.map(item => ({ label: item, value: item }));
const newCitiesToLoad = citiesToLoad.map(item => ({
label: item,
value: item
}));
return (
<div id="signupContainer" className="signinup-container">
<h3 className="mb-4"> Sign Up </h3>
<Formik
initialValues={{
state: selectedState,
city: ""
}}
validationSchema={Yup.object().shape({
state: Yup.string().required("Please select state."),
city: Yup.string().required("Please select city.")
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Getting form values - ", values);
setSubmitting(false);
}, 500);
}}
enableReinitialize={true}
>
{props => {
const {
values,
touched,
dirty,
errors,
isSubmitting,
handleChange,
setFieldValue,
setFieldTouched
} = props;
return (
<Form id="signUpForm" className="signinupForm" noValidate>
<div className="form-group">
<label htmlFor="state" className="form-label">
State
</label>
<Select
name="state"
id="state"
onBlur={() => setFieldTouched("state", true)}
value={values.state}
onChange={(opt, e) => {
this.handleState(opt);
handleChange(e);
setFieldValue("state", opt.value);
}}
options={newStateList}
error={errors.state}
touched={touched.state}
/>
</div>
<div className="form-group">
<label htmlFor="city" className="form-label">
City
</label>
<Select
name="city"
id="city"
onBlur={() => setFieldTouched("city", true)}
value={values.city}
onChange={(opt, e) => {
this.handleMyCity(opt);
setFieldValue("city", opt.value);
}}
options={newCitiesToLoad}
/>
</div>
{isSubmitting ? (
<span className="loader-gif">
<img src={loading} alt="Loading..." />
</span>
) : null}
<button
type="submit"
className="btn btn-filled"
disabled={!dirty || isSubmitting}
>
Submit
</button>
{/*Submit */}
</Form>
);
}}
</Formik>
</div>
);
}
}
export default SelectForm;
Upon selecting any value from the selecet dropdown, my selected value should appear in select box
You are setting the field value on onchange of select setFieldValue("state", opt.value); so you don't need to set value for the <Select>:
<Select
name="state"
id="state"
onBlur={() => setFieldTouched("state", true)}
onChange={(opt, e) => {
this.handleState(opt);
handleChange(e);
setFieldValue("state", opt.value);
}}
options={newStateList}
error={errors.state}
touched={touched.state}
/>
change for the both <Select>
react-select accepts an object as a value so you need to pass an object of
let object = {
"label": "Andhra Pradesh",
"value": "Andhra Pradesh"
}
bypassing an object in value the selected value appears in the select box
Here is a codesandbox demo https://codesandbox.io/s/floral-fire-8txrt
so updated code is
import React, { Component } from "react";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";
const debug = true;
class SelectForm extends Component {
constructor(props) {
super(props);
this.state = {
stateList: [],
stateCity: "",
selectedState: "",
citiesToLoad: []
};
}
handleState(opt) {
console.log(opt.value);
let citiesList = [];
Object.keys(this.state.stateCity).forEach(key => {
if (key === opt.value) {
this.state.stateCity[key].map((cityName, j) => {
citiesList.push(cityName);
});
}
});
this.setState({
selectedState: opt,
citiesToLoad: citiesList
});
}
handleMyCity(opt) {
console.log(opt.value);
}
componentDidMount() {
let stateLi = [];
fetch(`stateCity.json`)
.then(response => {
console.log(response);
return response.json();
})
.then(data => {
console.log(data);
for (let key in data) {
if (data.hasOwnProperty(key)) {
stateLi.push(key);
}
}
this.setState({ stateCity: data, stateList: stateLi });
})
.catch(err => {
console.log("Error Reading data " + err); // Do something for error here
});
}
render() {
const { selectedState, stateList, citiesToLoad } = this.state;
const newStateList = stateList.map(item => ({ label: item, value: item }));
const newCitiesToLoad = citiesToLoad.map(item => ({
label: item,
value: item
}));
return (
<div id="signupContainer" className="signinup-container">
<h3 className="mb-4"> Sign Up </h3>
<Formik
initialValues={{
state: selectedState,
city: ""
}}
validationSchema={Yup.object().shape({
state: Yup.string().required("Please select state."),
city: Yup.string().required("Please select city.")
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Getting form values - ", values);
setSubmitting(false);
}, 500);
}}
enableReinitialize={true}
>
{props => {
const {
values,
touched,
dirty,
errors,
isSubmitting,
handleChange,
setFieldValue,
setFieldTouched
} = props;
return (
<Form id="signUpForm" className="signinupForm" noValidate>
<div className="form-group">
<label htmlFor="state" className="form-label">
State
</label>
<Select
name="state"
id="state"
onBlur={() => setFieldTouched("state", true)}
value={values.state}
onChange={(opt, e) => {
this.handleState(opt);
handleChange(e);
setFieldValue("state", opt);
}}
options={newStateList}
error={errors.state}
touched={touched.state}
/>
</div>
<div className="form-group">
<label htmlFor="city" className="form-label">
City
</label>
<Select
name="city"
id="city"
onBlur={() => setFieldTouched("city", true)}
value={values.city}
onChange={(opt, e) => {
this.handleMyCity(opt);
setFieldValue("city", opt);
}}
options={newCitiesToLoad}
/>
</div>
{isSubmitting ? (
<span className="loader-gif">
<img src={loading} alt="Loading..." />
</span>
) : null}
<button
type="submit"
className="btn btn-filled"
disabled={!dirty || isSubmitting}
>
Submit
</button>
{/*Submit */}
</Form>
);
}}
</Formik>
</div>
);
}
}
export default SelectForm;

Resources