React First Item in a TextField Select does not get Selected - javascript

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);

Related

Formik - Select first option in dropdown list when component gets loaded

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.

MUI Autocomplete remove option after selection

I have a Material-UI Autocomplete component. In order to prevent the user from selecting the same element twice (it would double id numbers) I'd like the element removed from the drop down entirely.
For example, if "Shawshank Redemption" was selected, it should get added to the list and be removed from the drop down entirely but not change the JSON data.
I've tried using a filter on filterOptions, but that doesn't seem to be working.
import React, { useState } from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import HighlightOffIcon from "#material-ui/icons/HighlightOff";
import IconButton from "#material-ui/core/IconButton";
export default function Playground() {
const defaultProps = {
options: top100Films,
getOptionLabel: (option) => option.title,
filterOptions: (options, state) => {
let newOptions = [];
options.forEach((option) => {
if (option.title.includes(state.inputValue)) {
newOptions.push(option);
}
});
return newOptions.filter((movie) => !movies.includes(movie));
}
};
const [movies, setMovies] = useState([]);
const [key, setKey] = useState(0);
return (
<div style={{ width: 300 }}>
<Autocomplete
{...defaultProps}
id="select-on-focus"
renderInput={(params) => (
<TextField {...params} label="movies" margin="normal" />
)}
onChange={(e, movie) => {
if (movie) {
// this line prevents an error if no movie is selected
setMovies([...movies, movie.title]);
}
// this setKey is supposed to clear the Autocomplete component by forcing a rerender.
// Works in my project but not here.
setKey(key + 1);
}}
/>
<List>
{movies.map((movie) => (
<ListItem key={movie.title}>
<ListItemText primary={movie} />
<IconButton
key={key}
aria-label="delete"
onClick={() => {
setMovies(() => movies.filter((m) => m !== movie));
}}
>
<HighlightOffIcon />
</IconButton>
</ListItem>
))}
</List>
</div>
);
}
// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
{ title: "The Shawshank Redemption", year: 1994 },
{ title: "The Godfather", year: 1972 },
{ title: "The Godfather: Part II", year: 1974 },
{ title: "The Dark Knight", year: 2008 },
];
See also: https://codesandbox.io/s/autocomplete-remove-from-list-5dvhg?file=/demo.js:0-6677
Edit: My project just got updated to MUI5, so I'm working on getting full functionality back, then I'll tackle this problem.
Set Autocomplete mode to multiple and turn on filterSelectedOptions to remove the selected option in the dropdown list. To display a list of selected options properly outside of the Autocomplete input, see this other answer.
const [movies, setMovies] = useState([]);
<Autocomplete
{...defaultProps}
multiple
filterSelectedOptions
renderTags={() => null} // don't render tag in the TextField
value={movies}
onChange={(e, newValue) => setMovies(newValue)}
/>
Live Demo
In order to prevent the user from selecting the same element twice (it would double id numbers) I'd like the element removed from the drop down entirely.
How about disabling them so that they cannot be selected again?
You can pass a getOptionsDisabled just like the getOptionLabel

useState for Dropdown elements - ReactJS

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.

Fix .map is not a function in a functional component

I have a Textbox where I set array items to the textbox and I use it as a drop down. My code works and I am able to see the items in the drop down, however when i click on any other item apart from the first item I get the error groups.map is not a function.
I set array from the API to my groups and then I try to map the items that I need into the textbox.
Please see my code below and assist me to fix it.
I set my state to an empty array here
const [groups, setGroups] = useState([]);
const handleChange = event => {
setGroups({
...groups,
[event.target.name]: event.target.value
});
};
I handle my API call here
axios
.post('http://', formData, config)
.then((response) => {
console.log(response.data)
setGroups(response.data);
})
.catch((error) => {
console.log(error.response);
});
}, []);
I render with my data to show in the drop down here
<TextField
fullWidth
label="Select Group"
margin="dense"
name="groups"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{ native: true }}
value={groups.name}
variant="outlined"
>
{groups.map(option => (
<option
key={option.id}
>
{option.name}
</option>
))}
</TextField>
It is because axios is not done with the request and your state has not been updated yet when you try to map over your state. Try this piece of code near the map:
{groups && groups.map(option => (
<option
key={option.id}
>
{option.name}
</option>
))}
The map will go over the array only if groups exists
Try adding a new state variable to store the selected group.
const [selected, setSelected] = useState();
Your handleChange function will become something like
const handleChange = event => {
setSelected(event.target.value);
};
And your TextField component will have it's value changed to match the corresponding state variable:
<TextField
fullWidth
label="Select Group"
margin="dense"
name="groups"
onChange={handleChange}
required
select
// eslint-disable-next-line react/jsx-sort-props
SelectProps={{ native: true }}
value={selected}
variant="outlined"
>
{groups.map(option => (
<option
key={option.id}
>
{option.name}
</option>
))}
</TextField>
I had same problem look for response that you are getting if it is an object you can convert it to array in api by
Object.entries(images)
Then send this response also in client side when you request data set data with res.data otherwise it will be huge object.....

MaterialUI Select set value is always out of range

i've a MaterialUI Select code, and i'm handling the value parameter dynamically. My problem is, when i set any value, it says always it's out of range, even showing the value in the valid values.
SelectInput.js:291 Material-UI: you have provided an out-of-range value `100001,250000` for the select (name="followers") component.
Consider providing a value that matches one of the available options or ''.
The available values are `0,50000`, `50001,100000`, `100001,250000`, `250001,500000`, `500001,750000`, `750001,9007199254740991`.
(anonymous) # SelectInput.js:291
And this is my code simplified:
const followers = [
{ '0-50k': [0, 50000] },
{ '50k-100k': [50001, 100000] },
{ '100k-250k': [100001, 250000] },
{ '250k-500k': [250001, 500000] },
{ '500k-750k': [500001, 750000] },
{ '+1M': [750001, Number.MAX_SAFE_INTEGER] },
];
<div className={classes.formGroup}>
<InputLabel id="followersL">Followers</InputLabel>
<Select
className={classes.field}
fullWidth
id="followers"
labelId="followersL"
margin="dense"
displayEmpty
name="followers"
onChange={(event) => setValue(event.target.value)} //I've updated the sate with the new value
value={
filters.basicInfo.followers
? value
: ''
}
variant="outlined"
>
{followers.map((element) => (
<MenuItem
value={element[Object.keys(element)]}
key={Object.keys(element)[0]}
>
{Object.keys(element)[0]}
</MenuItem>
))}
</Select>
</div>
As you can see in the message, the value selected 100001,250000 it's inside the range examples 100001,250000
Where is the problem?
add this defaultValue = "" like this
<Select
...
defaultValue=""
>
This advice may be useful for others:
If the value for Select element is object, it should be the exact instance of the object from the list of Options.
For example:
const [test, setTest] = useState("");
//list of options for Material UI select
const testOptions = [
{name: "123"},
{name: "456"},
{name: "769"},
];
//let's try to change value to {name: "123"} using JS
setTest(testOptions[0]); // everything is OK
setTest({name: "123"}); // Error! You provided out of range value!
Stringifying your value will get this to work.
element[Object.keys(element)] + ""}
If you needed it to be in its original array format before sending the result to your server you could use a function like this to do this
const strToArr = str => str.split(',').map(item => Number(item))
In my code here I have used your provided example and been able to replicate your error. But Stringifying the value removes the error and gets it to work as expected.
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import InputLabel from "#material-ui/core/InputLabel";
import MenuItem from "#material-ui/core/MenuItem";
import Select from "#material-ui/core/Select";
const useStyles = makeStyles(theme => ({
formControl: {
margin: theme.spacing(1),
minWidth: 120
},
selectEmpty: {
marginTop: theme.spacing(2)
}
}));
export default function SimpleSelect() {
const classes = useStyles();
const followers = [
{ "0-50k": [0, 50000] },
{ "50k-100k": [50001, 100000] },
{ "100k-250k": [100001, 250000] },
{ "250k-500k": [250001, 500000] },
{ "500k-750k": [500001, 750000] },
{ "+1M": [750001, Number.MAX_SAFE_INTEGER] }
];
const [value, setValue] = React.useState("");
const handleChange = event => setValue(event.target.value);
return (
<div>
<p>value - {value}</p>
<div className={classes.formGroup}>
<InputLabel id="followersL">Followers</InputLabel>
<Select
className={classes.field}
fullWidth
id="followers"
labelId="followersL"
margin="dense"
displayEmpty
name="followers"
onChange={handleChange}
value={value}
variant="outlined"
>
{followers.map(element => (
<MenuItem
value={element[Object.keys(element)] + ""}
key={Object.keys(element)[0]}
>
{Object.keys(element)[0]}
</MenuItem>
))}
</Select>
</div>
</div>
);
}
I ran into the same problem (you have provided an out-of-range value) when using a number state with a default value of -1:
const [selectedAccountId, setSelectedAccountId] = useState<number>(-1);
The solution to this problem was to assign an empty string for the value property in Material's UI Select component instead of using the default value of -1:
value={selectedAccountId === -1 ? '' : selectedAccountId}
Full component example:
<FormControl fullWidth>
<InputLabel>Account</InputLabel>
<Select
id="account"
value={selectedAccountId === -1 ? '' : selectedAccountId}
onChange={event => {
setSelectedAccountId(Number(event.target.value));
}}>
{allAccounts.map((account, index) => (
<MenuItem key={index} value={account.id}>
{account.exchange} ({account.id})
</MenuItem>
))}
</Select>
</FormControl>
From some research, what I've come to understand to be the reason for this warning, in my case, is the MUI Select was trying to map over a list of options that were not available on the first render as the list was coming from Axios response.
I made a component named Dropdown that renders the MUI Select component. I was providing it four props:
options: the list of options,
initialValue: the default value as I
had different default values for different instances of the Dropdown component that were not the first item of the
options list always
... and 2 other props that are not scoped for this discussion.
So, for each instance of the Dropdown component, I had to check whether the options list has any data, and only then render it. And this is what removed the warning from the console. To give a glimpse of what I did:
{viewsData.length > 0 && (
<Dropdown
options={viewsData}
initialValue={7}
{...otherProps}
/>
)}
This was bugging me for a long time. Hopefully this will help someone.
I got the same error and I solved it by making sure that the default value and the other select values thereafter are the same, for example if the default value is a string like '' then the other values are objects it will show the warning so to avoid such a problem make the default value to be either a [] or {} or most preferably null
To add to #Ait Friha Zaid response.
I also added the defaultValue attribute but also added an additional option:
const values = ['option 1', 'option 2', 'option 3'];
<FormControl fullWidth>
<InputLabel>{title}</InputLabel>
<Select
defaultValue="choose"
label={title}
onChange={e => func({...state, [keyName]: e.target.value}) }
>
<MenuItem disabled value="choose">Choose Option</MenuItem>
{values.map((value) => (
<MenuItem value={value} key={value}>{value}</MenuItem>
))}
</Select>
</FormControl>
That way you always have a disabled option that works as a placeholder which is the default option, and in case you want to do form validation, until the user changes the option, the state wont be changed.

Categories