React | Single onchange method for multiple dynamic select box - reactjs

I'm getting response from API like below json. By using that response I'm generating dynamic select.
Issue:
Not able to select different value for different selectbox. I'm trying to achieve with state. How to declare state dynamically in this case.
JSON
const jsonFields = {
fields: [
{ name: "sex", value: ["m", "f"] },
{ name: "age", value: ["10", "20"] }
]
};
note: I don't know json key name like sex and age. name will be dynamic
Select Change:
handleChange = event => {
this.setState({ [event.target.name]: event.target.value });
};
Loading fields:
loadfields = () => {
const FieldsArray = [];
let FieldsData = jsonFields.fields;
FieldsData.forEach((val, index) => {
let FieldsDatavalue = val.value;
FieldsArray.push(
<FormControl>
<InputLabel htmlFor="controlled-open-select">{val.name}</InputLabel>
<Select
value=""
onChange={this.handleChange}
inputProps={{
name: "age"
}}
>
{FieldsDatavalue.map(option => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</Select>
</FormControl>
);
});
return FieldsArray;
};
Scenario : Code Sandbox

You're not storing the selected value in your state, and the name in inputProps is hard coded to 'age'. If you fix those problems, it works:
<Select
value={this.state[val.name] ? this.state[val.name] : ''}
onChange={this.handleChange}
inputProps={{name: val.name}}
>
Here's a CodeSandbox link.
UPDATE: The undefined values cause the label to overlap with the selected value, and since you retrieve them dynamically and cannot initialize them in the state, you can use a conditional this.state[val.name] ? this.state[val.name] : '' to fix the labels. (sandbox has been updated)

Related

why is react select default value not working

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"]}

Disable options based on currently selected option with Material UI Autocomplete component

I am creating an Autocomplete element using Material UI, React Hook Form, and Yup that allows a user to choose multiple inputs for the days of the week.
If a user chooses the "Every day" option, I want to disable being able to choose Sunday, Monday, Tues... etc. How would I do that using getOptionDisabled?
Here is my current code...
const daysOfWeekSuggestions = [
{label: "Every day"},
{label: "Sunday"},
{label: "Monday"},
{label: "Tuesday"},
{label: "Wednesday"},
{label: "Thursday"},
{label: "Friday"},
{label: "Saturday"}
];
<Autocomplete
disableClearable
disablePortal
filterSelectedOptions
multiple
getOptionLabel={(option) => option.label}
id="days-autocomplete"
options={daysOfWeekSuggestions}
renderInput={(params) => <TextField
required
error={errors.daysOfWeek ? true : false}
id="daysOfWeek"
label="Days of the week"
name="daysOfWeek"
type="search"
{...params}
{...register("daysOfWeek")}
/>}
/>
What you need to do is use the getOptionDisabled prop and then check to see if the user has already selected Every Day. Im guessing you are saving it into state similar to how I am using it here so you can then just use the .some function to see if the Every day object is in the values array.
const daysOfWeekSuggestions = [
{ label: "Every day" },
{ label: "Sunday" },
{ label: "Monday" },
{ label: "Tuesday" },
{ label: "Wednesday" },
{ label: "Thursday" },
{ label: "Friday" },
{ label: "Saturday" }
];
export default function DaysOfTheWeekSelect() {
const [value, setValue] = React.useState([]);
const handleOnChange = (e, value) => {
setValue(value);
};
return (
<Autocomplete
disableClearable
disablePortal
multiple
getOptionLabel={(option) => option.label}
id="days-autocomplete"
onChange={handleOnChange}
options={daysOfWeekSuggestions}
getOptionDisabled={(option) => {
if (value.some((day) => day.label === "Every day")) {
return true;
}
if (value.some((day) => day.label === option.label)) {
return true;
}
return false;
}}
value={value}
renderInput={(params) => (
<TextField
required
error={errors.daysOfWeek ? true : false}
id="daysOfWeek"
label="Days of the week"
name="daysOfWeek"
type="search"
{...params}
{...register("daysOfWeek")}
/>
)}
/>
);
}
I also made it that if you have already selected a value then it would be disabled as well. Just seems a little bit better than just filtering out the selected values
I think you need more than just getOptionDisabled option.
I have done similar thing in my prev app, for that you need to add one more property called disabled to each option and onChange to Every day, manually disable remaining options.
You can check working demo here - https://codesandbox.io/s/disable-mui-autocomplete-options-on-condition-gurzb
Try using the isOptionEqualToValue instead
<Autocomplete
...
isOptionEqualToValue={(option, value) => option.id === value.id}
...
/>
supply the identifying property, in this case 'id'

React: OnChange for drop down return me the entire <select></select> tag

I am working with react and I have a dropdown menu which is generated like that:
Select a user : <select defaultValue={'DEFAULT'} onChange={this.handleFilter}><option value="DEFAULT" disabled>-- select an gangster --</option>{ddUsers}</select>
ddUsers:
let ddUsers = this.state.users.map(user => <option key={uuid.v4()} value={user}>{user.name}</option>)
And here is my users array:
users : [
{ name: 'All', key : uuid.v4()},
{ name: 'Koko', key : uuid.v4()},
{ name: 'Krikri', key : uuid.v4()},
{ name: 'Kéké', key : uuid.v4()}
]
My problem is that when I want to use the event.target it return me the entire select tag (here the output of console.log(event.target):
<select>
​<option value=​"DEFAULT" disabled selected>​-- select an gangster --​</option>
​<option value=​"[object Object]​">​All​</option>​
<option value=​"[object Object]​">​Koko​</option>​
<option value=​"[object Object]​">​Krikri​</option>​
<option value=​"[object Object]​">​Kéké​</option>​
</select>​
where it should normally return me only the user.
here is my handleUser (which displays me select tag above):
handleFilter = (event) => {
console.log(event.target);
}
I am lost on what I am missing. I have something similar and it's working perfectly.
What you want is a key/value at select value, but unfortunately, it does not work.
Maybe you'll need to use another component like React-select or use JSON.stringfy() and JSON.parse()
I have made an abstraction code using JSON methods.
const uuid = {
v4() {
return Math.random();
}
};
const users = [
{ name: "All", key: uuid.v4() },
{ name: "Koko", key: uuid.v4() },
{ name: "Krikri", key: uuid.v4() },
{ name: "Kéké", key: uuid.v4() }
];
function App() {
const [selected, setSelected] = React.useState("");
function parseSelected(event) {
const valueToParse = event.target.value;
const itemSelected = JSON.parse(valueToParse);
setSelected(itemSelected);
return;
}
return (
<div>
<select name="any" id="any" onChange={parseSelected}>
{users.map(user => (
<option key={user.key} value={JSON.stringify(user)}>
{user.name}
</option>
))}
</select>
<p>Selected name: {selected.name}</p>
<p>Selected key: {selected.key}</p>
</div>
);
}
the reason is Option HTML interface accepts only DOMString as a value.
You can see the docs here
You could do something like this.. This allows for flexibility so you can use this <select> component anywhere, using any object with any property.. (using #CarlosQuerioz answer, you'd always have to use an object with a name key)
Parameter meanings:
data: array you are wanting to use as options for select
value: the key from your data property that you want to display
placeholder: placeholder value (not selectable)
defaultOption: default selected option (more on this below)
onSelectChange: event that is fired when selection changes
How we handle defaultOption:
If you pass in an array of objects to defaultOption we will select the first object in that array, and use the specified value key to determine the value to be shown
If you pass in an object, we will show the specified value key
To demonstrate the defaultOption, uncomment each of these values in the example below, you'll see what I mean.
const users = [ // If this was your `data` object
{ name: "Karkar", key: 1 },
{ name: "Koko", key: 2 },
{ name: "Krikri", key: 3 },
{ name: "Kéké", key: 4 }
];
<MySelect
data={users}
value="name" // Must be a valid key that your objects have
onSelectChange={handle}
defaultOption={users[0].name}
//defaultOption={users[1]} // Would display 'Koko' by default
//defaultOption={users} // Would display 'Karkar' by default
/>
EXAMPLE:
const { useState, useEffect } = React;
const { render } = ReactDOM;
function MySelect({ data, value, defaultOption = "", onSelectChange, placeholder = "Select an option" }) {
const [selected, setSelected] = useState(defaultOption);
const handleOnSelectChange = selection => {
onSelectChange && onSelectChange(selection);
}
const handleChange = event => {
let sel = data.find(d => d[value] === event.target.value);
setSelected(sel);
handleOnSelectChange(sel);
};
useEffect(() => {
if (typeof selected === "object") {
let val = selected.length > 0 ? selected[0] : selected;
setSelected(val);
handleOnSelectChange(val);
}
}, []);
return (
<select value={selected[value]} onChange={handleChange}>
<option value="" disabled>
{placeholder}
</option>
{data && data.map((itm, indx) => (
<option key={indx} value={itm[value]}>
{itm[value]}
</option>
))}
</select>
);
}
const users = [
{ name: "Karkar", key: 1 },
{ name: "Koko", key: 2 },
{ name: "Krikri", key: 3 },
{ name: "Kéké", key: 4 }
];
function App() {
const [user, setUser] = useState();
const handle = selectedUser => {
setUser(selectedUser);
};
return (
<div>
<MySelect
data={users}
value="name"
placeholder="Please make a selection"
onSelectChange={handle}
//defaultOption={users[0].name}
//defaultOption={users[0]}
defaultOption={users}
/>
{user && <pre>{JSON.stringify(user, null, 2)}</pre>}
</div>
);
}
render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>

when using {react-select} Cannot read property 'name' of undefined

I am very beginning to reactJS and front end
I added react-select npm for my dropdown like below, before added react-select everything is working fine. How to define name in Select?
<div className="container">
<div className="row">
<div className="col-md-4" />
<div className="col-md-4">
<Select
options={this.state.allGenres}
onChange={this.props.onDataChange}
name="genre"
/>
</div>
<div className="col-md-4" />
</div>
</div>
this is my array,
var Data = response.data;
const map = Data.map((arrElement, index) => ({
label: arrElement,
value: index
}));
example:
[
{
"label": "Action",
"value": 0
},
{
"label": "Comedy",
"value": 1
},
{
"label": "Documentary",
"value": 2
}
]
error message coming in here,
dataChange(ev, action) {
this.setState({
[ev.target.name]: ev.target.value
});
}
render()
render() {
return (
<Movie
onPostData={this.postData.bind(this)}
onDataChange={this.dataChange.bind(this)}
/>
);
}
Error
Uncaught TypeError: Cannot read property 'name' of undefined
at Movies.dataChange
You expect the first argument in react-select´s onChange method to be an event object, but it isn't.
The first argument is the selected option (or options if you have isMulti set).
There is also a second argument which is an object with the following attributes:
action: The action which triggered the change
name: The name given to the Select component using the name prop.
So if you want to use the name:
onDataChange={(value, action) => {
this.setState({
[action.name]: value
})
}}
Reference in source code
I worked around this method and it worked.
handleSelectChange: function(name) {
return function(newValue) {
// perform change on this.state for name and newValue
}.bind(this);
},
render: function() {
return (
<div>
<Select ...attrs... onChange={this.handleSelectChange('first')} />
<Select ...attrs... onChange={this.handleSelectChange('second')} />
</div>);
}
This is how you can get the value(s) of the selected option(s) and the name of the input as well.
For more info, check this issue on Github.
handleChange = (selectedOptions, actionMeta) => {
const inputName = actionMeta.name;
let selectedValues;
if (Array.isArray(selectedOptions)) {
// An array containing values of the selected options (like: ["one", "two", "three"]
selectedValues = selectedOptions.map(option => option.value);
// OR, use this if you want the values of the selected options as a string separated by comma (like: "one,two,three")
selectedValues = selectedOptions.map(option => option.value).join(",");
} else {
// An array containing the selected option (like: ["one"])
selectedValues = [selectedOptions.value];
// OR, use this if you want the value of the selected option as a string (like: "one")
selectedValues = selectedOptions.value;
}
// Do whatever you want with the selected values
//..
this.setState({
[inputName]: selectedValues
});
}
<Select
name="genre"
options={this.state.allGenres}
onChange={this.handleChange}
/>

Select React Component data option not appeard

I am facing an issue with representing data inside select-react Component, I had successfully getting data form server side (node js ) by componentDidMount()
componentDidMount(){
fetch('api/transporationTypes')
.then( res => res.json())
.then(trasnportation => this.setState({trasnportation }, () => console.log(trasnportation)));
}
but I cannot set loaded data inside React-Select Component I tried the below here below how this component works with static data.
render() {
const { selectedOption } = this.state;
return (
<Select
name="form-field-name"
value={selectedOption}
onChange={this.handleChange}
options={[
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' },
]}
/>
);
}
}
but when I trying to load dynamic data with code below it represent No results Found see screenshot: http://prntscr.com/jqvp76.
alos printing the data via console.log('optionItems',optionItems) in dev tools print correctly http://prntscr.com/jqvq7v
How can I make option of select component works successfully
render() {
const { selectedOption } = this.state.selectedOption;
let optionItems = this.state.trasnportation.map((trans) =>
[ {value: `${trans.TransportationType}` , label : `${trans.TransportationType}`}]
);
console.log('optionItems',optionItems)
return (
<div className="row">
<h1>Choose Tranportation Type</h1>
<Select className="col-md-8"
name="form-field-name"
value={selectedOption}
onChange={this.handleChange1}
option={optionItems}
placeholder = "Select one of below"/>
</div>
);
}
}
Thanks -- Fadi
The items have wrong type:
let optionItems = this.state.trasnportation.map((trans) =>
[ {value: `${trans.TransportationType}` , label : `${trans.TransportationType}`}]
);
to
let optionItems = this.state.trasnportation.map((trans) =>
({value: `${trans.TransportationType}` , label : `${trans.TransportationType}`})
); // [] -> ()
It likely has something to do with the typos, in the code supplied there is:
trasnportation
transporation
transportation

Resources