As I understood, in react-select (latest) you need to send an object to the value props, to select/preselect an option. I am also using react-hook-form for my form management.
Is it possible to set the value through react-select or do I need to use react-hook-form's setValue()
As I click on an Item, I send this to my states:
const handleSectionOnClick = (id) => {
setTravelRoute({
'LocationEnd': { value: +travelRoutes[id].LocationIdEnd, label: travelRoutes[id].LocationCityEnd },
'LocationStart': { value: +travelRoutes[id].LocationIdStart, label: travelRoutes[id].LocationCityStart },
'startDate': new Date(),
'endDate': new Date()
})
}
My Demo object looks like this:
{
'LocationCityEnd': "Berlin, Ger",
'LocationCityStart': "Hattingen, Ger",
'LocationIdEnd': 2,
'LocationIdStart': 1,
'startDate': new Date(),
'endDate': new Date()
}
My (LocationStart) select component looks like this:
<Controller
render={({ onChange }) => (
<Select
styles={customStyles}
onChange={handleOnChangeStartLocation}
name="startLocation"
value={travelRoute?.LocationStart}
options={selectOptions}
placeholder="Choose..."
/>
)}
name="startLocation"
className={`${errors.startLocation ? 'inputSelectError' : ''}`}
control={control}
rules={{ required: true }}
/>
Nothing gets selected, not even the value/label.
What Am I missing? Thank you!
EDIT:
I added the handleOnChangeStartLocation function:
const handleOnChangeStartLocation = e => {
const { value, label } = e;
setTravelRoute(prevState => ({
...prevState,
LocationIdStart: value,
LocationCityStart: label
}))
}
The problem seems to be that you are not updating LocationStart with the new value/label. You should do something like:
const handleOnChangeStartLocation = e => {
const { value, label } = e;
setTravelRoute(prevState => ({
...prevState,
LocationStart: e, // add this line of code
LocationIdStart: value,
LocationCityStart: label
}))
}
Related
I created an array of objects that has form field definitions in order to quickly generate dynamic user forms, in react.
export const formFieldList = [
{
fieldType: 'input',
inputType: 'date',
name: 'LastLoginDate',
id: 'LastLoginDate',
label: 'Last Login Date',
value: moment(new Date()).format('YYYY-MM-DD'),
classWrapper: 'col-md-3',
}]
The problem occurred when I changed this constant array to a function. I need to get some data from async functions so I changed this array to a async func as well. All other form fields (text, select, checkbox) still work well but date fields, formik doest get date value after then.
export const formFieldList = async () => ([
{
fieldType: 'input',
inputType: 'date',
name: 'LastLoginDate',
id: 'LastLoginDate',
label: 'Last Login Date',
value: moment(new Date()).format('YYYY-MM-DD'),
classWrapper: 'col-md-3',
},
{
fieldType: 'select',
name: 'CurrencyId',
id: 'CurrencyId',
label: 'Currencies',
classWrapper: 'col-md-3',
options: await getCurrencies()
}]
If I write date value as hard-coded, it doesnt change anything, I mean function returns everything well, the problem is formik doesnt get that value as a prop when it returns from a function. My input component that get that values:
<input
{...formik.getFieldProps(Field.name)}
type={Field.inputType}
placeholder={Field.placeholder}
name={Field.name}
id={Field.id || Field.name}
/>
In the DOM, input element is shown as <input value name='xx' ... />.
My FormGenerator component that takes formFieldList and returns it to form:
const FormGenerator:FC<Props> = ({formFieldList, formButton, onSubmitValues}) => {
...
let initialValues = {}
for (const Field of formFieldList) {
if (Field.name !== 'newLine'){
initialValues = {...initialValues, [Field.name]: Field.value}
}
}
const schema = formFieldList.reduce(getValidationSchema, {})
const validationSchema = Yup.object().shape(schema)
const formik = useFormik({
initialValues,
enableReinitialize: true,
validationSchema,
onSubmit: async (values, {setStatus, setSubmitting}) => {
setLoading(true)
try {
onSubmitValues(values)
setLoading(false)
} catch (error) {
console.error(error)
setStatus('Some errors occurred')
setSubmitting(false)
setLoading(false)
}
},
})
return (
...
<form
onSubmit={formik.handleSubmit}
noValidate
>
{ formFieldList?.map((Field, index) => {
const touchedField = Field.name as keyof typeof formik.touched;
const errorsField = Field.name as keyof typeof formik.errors;
switch (Field.fieldType) {
case 'input':
return <InputField Field={Field} formik={formik} touchedField={touchedField} errorsField={errorsField} key={Field.name}/>
case 'select':
return <SelectField Field={Field} formik={formik} key={Field.name}/>
case 'newLine':
return <div className={Field.classWrapper} key={index}></div>
}
})}
...
</form>
</>
)
}
Updating:
After adding 'enableReinitialize' (with help of #Sheraff), form show dates and works well, but console give me an error as 'A component is changing an uncontrolled input to be controlled...'. Then I added value property to input element to make it controlled. But now date picker doesnt update ui when I change date manuelly (it returns correct date as its value). If I enter value 'undefined' in formFieldList then it works again, but this time I coundt initialize my date dynamically.
How can I fix it?
I am trying to use react select and trying to set a defaultValue but when I set a default value it does not appear on my dropdown. I have tried everything and then came here for help. any help would be appreciated. below attached is the code :
<Select
classNamePrefix="react-select"
options={
countryDetails
? countryDetails.map((c) => ({
label: c.label.split("-")[0],
value: c.value
}))
: [{ label: "", value: "" }]
}
name="operatingCountry"
placeholder={t("Select Country")}
components={{ ValueContainer: CustomValueContainer }}
defaultValue={
(console.log(
"country boy",
countryDetails.find((c) => c.value == defaultCountryUser)
),
countryDetails.find((c) => c.value == defaultCountryUser))
}
value={this.props.basicInfo["operatingCountry"].value}
onChange={({ label, value }) => {
this.props.selectChanged(
"operatingCountry",
{ label, value },
sectionKey
);
}}
/>
the console in the defaultValue gives result in console. as an object {label:"two",value:2}
The value should be an object. Try changing value to this:
value={this.props.basicInfo["operatingCountry"]}
when i try to change the value in autocomplete of material-ui, i always get its value 0, here i have uploaded my whole code, can anyone please check my code and help me to resolve this issue ?
any help will be really appreciated.
export default function CreatePIC() {
const classes = useStyles();
const Department_list = [
{ label: 'Department1', id: 1 },
{ label: 'Department2', id: 2 },
{ label: 'Department3', id: 3 },
{ label: 'Department4', id: 4},
{ label: 'Department5', id: 5 }
]
const [department, setDepartment] = React.useState('');
const handleChangeDepartment = (event) => {
console.log(event.target.value);
setDepartment(event.target.value);
};
return (
<Autocomplete
id="Department"
value={department}
helperText={error.department}
options={Department_list}
getOptionLabel={option => typeof option === 'string' ? option : option.label}
onChange = {handleChangeDepartment}
renderInput={(params) => <TextField {...params} label="Search Department" variant="outlined" placeholder="Add Department" />}
/>
)
}
Ciao, in Autocomplete component event.target.value will be always 0. If you want to get the selected department you could use value in handleChangeDepartment. So your code becomes:
const handleChangeDepartment = (event, values) => {
console.log(event.target.value); // print always 0
console.log(values); // print values selected like { label: 'Department1', id: 1 }
setDepartment(values.label); // set department with values.label
};
Here a codesandbox example.
Rather than using:
event.target.value
try using:
event.target.innerText
or, to find the option index, use:
event.target.dataset.optionIndex
I have a config file with some fields for generating input elements inside a component.
I'm trying to generate an AsyncSelect input field and assigning it loadOptions prop through the config. Problem is that the function never gets called.
Here's the object in the configuration for generating the AsyncSelect input:
{
key: 'cityCode',
value: (customer, borrower) => ({
label: borrower.cityName,
value: borrower.cityCode
}),
text: 'city',
disabled: falseFn,
input: REACT_ASYNC_SELECT_INPUT,
props: (borrower, component) => ({
inputValue: component.state.cityName,
loadOptions: () => {
return CitiesService.find(component.state.cityName || '')
.then(records => records.map(record => ({
label: record.name,
value: record.code
})));
},
onInputChange: cityName => {
component.setState({
cityName
});
},
onChange: ({label, value}, {action}) => {
if (action === 'clear') {
component.updateCustomer(component.props.fieldKey + 'cityName', '');
component.updateCustomer(component.props.fieldKey + 'cityCode', -1);
} else {
component.updateCustomer(component.props.fieldKey + 'cityName', label);
component.updateCustomer(component.props.fieldKey + 'cityCode', value);
}
}
}),
render: trueFn
},
Here's the part of the component render utilizing the config file to render different inputs:
<div className="values">
{
BorrowerInfoConfig().map(field => {
if (field.render(borrower)) {
const kebabCaseKey = _.kebabCase(field.key);
const fieldElement = React.cloneElement(field.input, {
className: `${kebabCaseKey}-input`,
value: field.value ? field.value(customer, borrower) : _.get(borrower, field.key),
onChange: e => {
let value = e.target.value;
if (field.options) {
value = Number(value);
}
this.updateCustomer(fieldKey + field.key, value);
},
disabled: field.disabled(borrower),
...field.props(borrower, this)
}, field.options ? Object.keys(field.options).map(option => <option
key={option}
className={`${kebabCaseKey}-option`}
value={option}>
{field.options[option]}
</option>) : null);
return <div key={field.key} className={`value ${kebabCaseKey}`}>
<span>{field.text}</span>
{fieldElement}
</div>;
}
return null;
})
}
</div>
As you can see I use React.cloneElement to clone the input from the config file and assign new properties to it depends on what I get from the config file, in this case 4 custom props:
inputValue
loadOptions
onInputChange
onChange
My problem is that loadOptions is never called, any ideas why? On the other hand inputValue is assign correctly to the cityName state of the component.
My Problem was that REACT_ASYNC_SELECT_INPUT references normal Select and not Select.Async.
I want to change it by using contact-select, but I keep getting an error.
When I select this option, I can see this error. help me plz.
error
this is options
const options = [
{ value: 'descript', label: '주관식' },
{ value: 'choice', label: '객관식' },
{ value: 'combine', label: '다중식' }
];
and this is onChange fuction
onChangeTmpType = (e) => {
this.setState({
tmp_type: e.target.value
})
}
this is React-Select
<Select
components={makeAnimated()}
value={this.state.tmp_type}
onChange={this.onChangeTmpType}
options={options}
/>
As specified in the documentation here, the onChange function looks like this:
function (
One of <
Object,
Array<Object>,
null,
undefined
>,
{
action required One of <
"select-option",
"deselect-option",
"remove-value",
"pop-value",
"set-value",
"clear",
"create-option"
>
}
) => undefined
and the e const you declare actually has the following structure:
{
label: ...,
value: ...
}
so no target key here but directly e.value if what you want is to access the props value.
Here a live example with console.log so you can see what's happening.
Replace your onChangeTmpType function as follows.
onChangeTmpType = (e) => {
this.setState({
tmp_type: e.value
})
}
The reason for this as (#Laura mentioned before) is that the e from react select only contains the value and the label .