Problem
When I select an option the the dropdown menu, nothing gets selected as the value of role is an empty string. The UI framework used is Semantic UI.
How the program works
When the user inputs on the Input fields the function onChange takes the input and savges it onto the empty string in values. However this is invalid when the Input type is a Dropdown Menu with predefined inputs. Hence, when the user selects any of the options it automatically gets turned to an empty string.
I am not able to understand how to solve this issue.
The Code
const [values, setValues] = useState({
username: '',
password: '',
confirmPassword: '',
email: '',
role: '' // This here seems to be the issue.
})
//OnChange Function (works for Input types)
const onChange = (e) => {
setValues({...values, [e.target.name]: e.target.value});
}
//FormInput for reference (working)
<Form.Input
label="Full Name"
placeholder="Full Name"
type="text"
name="username"
value={values.username}
onChange={onChange}
/>
//DropDown menu from SemanticUI
<Dropdown
placeholder="Select Role"
name="role"
fluid
selection
options={options}
value={values.role}
onChange={onChange}
/>
//Options for Dropdown Menu
const options =[
{
key: 'Client',
text: 'Client',
value: 'Client'
},
{
key: 'Employee',
text: 'Employee',
value: 'Employee'
}
]
Update: added "name"=role in the dropdown menu. However still does not work.
Screenshot before selecting dropdown Item
Screenshot after selecting dropdown Item
Update : Added images for reference
Your Dropdown onChange should be something like this
const onChange = (e, {value}) => {
setValues({...values, role: value});
}
Refer to controlled input demo here: https://react.semantic-ui.com/modules/dropdown/#usage-controlled
EDIT: If you don't want to update your onChange function then perhaps this would do it.
<Dropdown
placeholder="Select Role"
fluid
selection
options={options}
value={values.role}
onChange={(e, {value}) => onChange({
target: {name: 'role', value}
})}
/>
I ran into the same issue with a form I'm making and my solution isn't DRY at all, but it works.
<select name="State" id="state" value={props.formInputs.state} onChange={props.handleChange}>
<option id="state" value="Alabama">AL</option>
<option id="state" value="Alaska">AK</option>
For each option in the drop down, I added the id="state" for my handleChange() and it was able to then take whatever option was selected from the drop down menu and put it into my object to pass to my database.
And my change handler and useState() hooks are below:
const [state, setState] = useState({
prayer_request:{
name: '',
state:'',
temptation:'',
}
})
const handleChange = (event) =>{
const updateInput = Object.assign({}, formInputs, { [event.target.id]:
event.target.value}, )
updateFormInputs(updateInput)
}
const [requests, setRequests] = useState([])
const [formInputs, updateFormInputs] = useState({
name: '',
state: state,
temptation: state,
})
<Dropdown
placeholder="Select Role"
name="role"
id="role"
fluid
selection
options={options}
value={values.role}
onChange={onChange}
/>
on Dropdown props:
add name="role"
Reason why you need to provide "name" prop on each form element to get value on onChange function.
onChange functions looks "e.target.name"
which is you didn't provide any "name" prop on your Dropdown Input.
Related
I have the following code which has a dropdown list with few values.
I want that when the component gets loaded, it selects the first option and let me submit right after.
I tried with the line below but no luck:
formik.setFieldValue(name, value);
Here you have the code:
import { ErrorMessage, Field, Form, Formik } from 'formik';
import { get } from 'lodash-es';
import React, { useEffect } from 'react';
import * as Yup from 'yup';
const DropdownListInput = (props) => {
useEffect(() => {
const firstOptionValue = get(props.children, '[0].props.value', '');
console.log({
name: props.field.name,
value: props.value,
firstOptionValue,
});
if (props.value === '' && firstOptionValue !== '') {
props.formik.setValues({
[props.field.name]: firstOptionValue,
});
}
}, []);
return (
<select
value={props.value}
onChange={(e) => props.handleChange(e.target.value)}
>
{props.children.map(({ props: { value, children: text } }, index) => (
<option value={value} key={index}>
{text}
</option>
))}
</select>
);
};
export default () => {
return (
<Formik
initialValues={{
email: '',
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required('Email is required.')
.email('Email is invalid.'),
})}
onSubmit={(values, { setSubmitting }) => {
console.log(values);
setSubmitting(false);
}}
enableReinitialize
validateOnMount
>
{(formik) => {
return (
<Form>
<div>
<Field
component={DropdownListInput}
formik={formik}
name="email"
value={formik.values.email}
handleChange={(value) => {
console.log(value);
formik.setFieldValue('email', value);
}}
>
<option value="bill.gates#microsoft.com">Bill Bates</option>
<option value="steve.jobs#apple.com">Steve Jobs</option>
<option value="elon.musk#tesla.com">Elon Musk</option>
</Field>
<ErrorMessage name="email">
{(error) => <div style={{ color: '#f00' }}>{error}</div>}
</ErrorMessage>
</div>
<input type="submit" value="Submit" disabled={!formik.isValid} />
</Form>
);
}}
</Formik>
);
};
Here you have the Stackblitz you can play with:
https://stackblitz.com/edit/react-formik-yup-example-uhdg-dt6cgk?file=Registration.js
Is there any way to select the first option automatically when the component gets loaded?
Requirements:
I need the Submit button to be enabled automatically.
Using initialValues is not an option for me because the dropdown is in the middle of a more complex context then it is the same dropdown who has to trigger the setting of that value.
If you want you can post your forked Stackblitz.
Thanks!
You should be able to just add the initial value as the default value. Since that would be selected by default, the initial value can reflect the same, and be changed on change.
initialValues={{
email: 'bill.gates#microsoft.com',
}}
This requirement is very straightforward where you always want to have a selected option in your dropdown, defaulting to the first one. To do that you have to set select attribute of tag just for first one. It's a pure HTML thing.
you can find the reference here:
<option value="bill.gates#microsoft.com" select>
Bill Bates
</option>
https://stackblitz.com/edit/react-formik-yup-example-uhdg-bgzdh7?file=Registration.js
Another scenario would be you want to preserve the user selection on rerenders, to do that you can rely on setting up initialValues to what user has selected.
In that way, if any selection is there reflect that or else default to the very first item of the dropdown.
I am using CustomInput from reactstrap. I am setting the type to select. In my CustomInput type="select" I am mapping over options from an array. I need to set the default value in the select to an particular option that is being mapped over. eg. Id like the default to be united states or germany, right now I am mapping over an array of different countries.
Here is my code below
<CustomInput
type="select"
id="country"
name="country"
className="mb-3"
value={country.countryCode}
onChange={handleChange}
>
{numberCountries.map(({countryCode, name}) => (
<Fragment>
<option key={countryCode} value={countryCode}>{name}</option> <--- how can i set a one of these options to be the default value rendered on the screen?
</Fragment>
))}
</CustomInput>
Here is the object I am mapping over
[
{
name: 'United States',
countryCode: 'US'
},
{
name: 'Germany',
countryCode: 'DE'
}
]
DOM might support a default value, but in a controlled input component, you have to come up with
<input value={value} onChange={onChange} />
And the value has to be the current value you selected. so if you ever change it based on the use selection, you have to set it via.
const onChange = e => {
setValue(e.target.value)
}
Which means the value is ALWAYS in sync with selection. This is different if you work with DOM directly.
Ok, now with this understanding, if you want to set some default stuff, you can do.
const [value, setValue] = useState(country.countryCode || defaultCode)
To be honest, even DOM offers a default behavior, you should want to override it. Because otherwise you'll end up with some bizarre behavior.
I have the loadProfile function that I want to call in useEffect. If the loadProfile function is called within useEffect, the name Mario should be displayed inside the input name field. How can I set the default value in the antd library inside input? I try to use defaultValue but the input field remains empty.
Example here: https://stackblitz.com/edit/react-311hn1
const App = () => {
const [values, setValues] = useState({
role: '',
name: '',
email: '',
password: '',
buttonText: 'Submit'
});
useEffect(() => {
loadProfile();
}, []);
const loadProfile = () => {
setValues({...values, role, name="Mario", email});
}
const {role, name, email, buttonText} = values;
const updateForm = () => (
<Form
{...layout}
name="basic"
initialValues={{
remember: true,
name: name
}}
>
<Form.Item
label= 'Name'
name='name'
rules={[
{
required: true,
message: 'Please input your name!',
},
]}
>
<Input
defaultValue= {name}
/>
</Form.Item>
<Form.Item
label="Email"
name="email"
value={email}
rules={[
{
type: 'email',
required: true,
message: 'Please input your email!',
},
]}
>
<Input
/>
</Form.Item>
<Form.Item {...tailLayout}>
<Button type="primary" htmlType="submit">
{buttonText}
</Button>
</Form.Item>
</Form>
);
return (
<>
<Row>
<Col span={24} style={{textAlign: 'center'}}>
<h1>Private</h1>
<p>Update Form</p>
</Col>
</Row>
{updateForm()}
</>
);
};
You have to make three changes to your code. This is a working component, I also extracted your component and put the appropriate state in there. I also made it functional.
https://stackblitz.com/edit/react-tlm1qg
First change
setValues({...values, role, name="Mario", email});
to
setValues({...values, name: "Mario"});
This will properly set the state.
Second change:
Next, you should notice that if you set defaultValue="test123" it still won't work, something is up. Remove name from Form.Item and boom it works. test123 shows up. But if you put values.name in there, it still doesn't work!
Third Change:
That's because defaultValue only sets that value right when the component is created, not on mount. So you have to use value={values.name} and it will set that value once on mount per your useEffect
In the demo component I also added a change handler for you so the user can type in there, if you wanted that.
If you look at the FAQ for Ant Design it says:
Components inside Form.Item with name property will turn into
controlled mode, which makes defaultValue not work anymore. Please try
initialValues of Form to set default value.
Ant Design is taking over control - so you don't have to set value={name} and onChange.
You want to set the values AFTER the component is created. So to do that you need
const [form] = Form.useForm();
React.useEffect(() => {
form.setFieldsValue({
username: 'Mario',
});
}, []);
and in you Form make sure you add:
<Form
{...layout}
name="basic"
initialValues={{
remember: true,
}}
form={form} // Add this!
>
I updated my online example.
Big picture - when you want to set the value after creation, use this hook and form.setFieldsValue
I have json payload that is passed as a key value pair. I need to populate the data in a TextField select which acts as a drop down. I am able to select the first or second options in the drop down if there is two or more items in the drop down. However, when there is a single item in the drop down it does not get selected even when I click on it.
This is my code below where I set my state as I am using a functional component:
const [departments, setDepartments] = useState([]);
const [selected, setSelected] = useState();
This is code below that checks if the item in the TextField is clicked:
const handleChange = event => {
setSelected(event.currentTarget.id);
};
Also this is my code below that I set the TextField with the data that I receive from the API:
<TextField
fullWidth
label="Select Department"
margin="dense"
name="departments"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{ native: true }}
value={selected}
variant="outlined"
>
{departments.map(option => (
<option
key={option.id}
value={option.id}
>
{option.department}
</option>
))}
</TextField>
Kindly help me resolve this. So I can get to set the first item in the drop down even if it is the only item in the drop down.
you had some error on your code like you used event.currentTarget.id but you should use event.target.value.
However, when there is a single item in the drop down it is not called handleChange when you click on it, because handleChange is onChange event. If you have one item, you cannot change item as there is only one, so onChange event is not fired. Instead, you can add another item like "Please select" then you can select your single item. Please check this example:
import React, {useEffect, useState} from "react";
import TextField from "#material-ui/core/TextField";
function TextFieldDDL() {
const [selected, setSelected] = useState();
const departments=[
{id: -1, department: 'Please Select...'},
{id: 1, department: 'CSE'},
// {id: 2, department: 'BBA'},
// {id: 3, department: 'EEE'}
];
function handleChange(e){
console.log(e.target.value, 'e');
setSelected(e.target.value);
}
return (
<React.Fragment>
<TextField
fullWidth
label="Select Department"
margin="dense"
name="departments"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{native: true}}
value={selected}
variant="outlined"
>
{departments.map(option => (
<option
key={option.id}
value={option.id}
>
{option.department}
</option>
))}
</TextField>
</React.Fragment>
);
}
export default TextFieldDDL;
Update: Add item into the list
const departments = [{id: -1, department: 'Please Select...'}, ...response.data];
setDepartments(departments);
i am developing a form in reactjs using formik plugin plugin link. when i submit form i am getting text fields values but dropdown values are coming empty...
this is my dropdown select
<div className="form-group">
<Field component="select" id="category" name="category" value={this.state.value} className={"form-control"} onChange={ this.handleChange }>
<option value="lokaler">Lokaler</option>
<option value="jobb">Jobb</option>
<option value="saker-ting">Saker & ting</option>
<option value="evenemang">Evenemang</option>
</Field>
</div>
handle submit function
export default withFormik({
enableReinitialize: true,
mapPropsToValues({ category }) {
return {
category: category || ''
}
},
handleSubmit(values, { setStatus, setErrors }){
console.log("data is this: ");
console.log(values); //here i am getting all form fields values except select value returning empty value
console.log("category: "+values.category);// here i always get empty value but not getting selected value
}
i am getting all text fields values in handle submit function but i am not able to get dropdown selected value....kindly tell me how to get dropdown select value in handle submit function ?
I also faced this problem yesterday. My problem was to submit form that contains ant design dropdown. I finally make it work after hours of:
revisiting the Formik Docs
watch Andrew Mead's video Better React Form with Formik again.
also viewing Jared Palmer's Working with 3rd-party inputs #1: react-select
The doc said you need to set onChange, onBlur events to setFieldValue, setFieldTouched props respectively like 3rd-party input example:
<MySelect
value={values.topics}
onChange={setFieldValue}
onBlur={setFieldTouched}
error={errors.topics}
touched={touched.topics}
/>
So to my problem, I need to change a bit:
<Select
{...field}
onChange={(value) => setFieldValue('fruitName', value)}
onBlur={()=> setFieldTouched('fruitName', true)}
value={values.fruitName}
...
>
...
</Select>
Hope this will help.
Here is my CodeSandbox
A more robust way to handle select components whilst using Formik would be to also use Jed Watsons react-select component. The two work together nicely and abstract away a lot of the boilerplate you would normally need to implement to get the component working seamlessly with Formik.
I have forked a simple example from Jared Palmer's react-select / Formik example on codesandbox and adjusted it to reflect your code above.
import "./formik-demo.css";
import React from "react";
import { render } from "react-dom";
import { withFormik } from "formik";
import Select from "react-select";
import "react-select/dist/react-select.css";
const options = [
{ value: "lokaler", label: "Lokaler" },
{ value: "jobb", label: "Jobb" },
{ value: "saker-ting", label: "Saker & ting" },
{ value: "evenemang", label: "Evenemang" }
];
const MyForm = props => {
const {
values,
handleChange,
handleBlur,
handleSubmit,
setFieldValue
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="myText" style={{ display: "block" }}>
My Text Field
</label>
<input
id="myText"
placeholder="Enter some text"
value={values.myText}
onChange={handleChange}
onBlur={handleBlur}
/>
<MySelect value={values.option} onChange={setFieldValue} />
<button type="submit">Submit</button>
</form>
);
};
class MySelect extends React.Component {
handleChange = value => {
// this is going to call setFieldValue and manually update values.topcis
this.props.onChange("option", value);
};
render() {
return (
<div style={{ margin: "1rem 0" }}>
<label htmlFor="color">Select an Option </label>
<Select
id="color"
options={options}
onChange={this.handleChange}
value={this.props.value}
/>
</div>
);
}
}
const MyEnhancedForm = withFormik({
mapPropsToValues: props => ({
myText: "",
option: {}
}),
handleSubmit: (values, { setSubmitting }) => {
console.log(values);
}
})(MyForm);
const App = () => <MyEnhancedForm />;
render(<App />, document.getElementById("root"));
There are a few gotchas, you have to include the react select css for the component to display properly
import "react-select/dist/react-select.css";
the function mapPropsToValues option field should be initialised to an object
mapPropsToValues: props => ({
myText: "",
option: {}
Finally here is a link to the codesandbox example