In my page, I have two <select> elements, where the options of the second one depends on the value of the first one.
In this case, the "problem" is that the options for a certain value in the first select are different from the options given when the first select has another value. Basically:
Alfa Romeo
Giulietta
Mito
Porsche
Cayenne
911
I've created a simple fiddle just to show you the example, and the problem: https://codesandbox.io/s/select-autochange-7bfj6
Please, open the console to see what I'm talking about. So, basically, at start 'Porsche' and '911' are selected. Then, if I change '911' to 'Cayenne' everything is good.
The problem is when I change 'Porsche' to 'Alfa': as it should be, the second select changes its value to 'Giulietta', BUT the onChange event of the second select is not triggered.
I'm mostly sure that the problem is some kind of de-synchronization between UI and state: in the state, the secondselect still has the value '911', but since that option is no longer available in the second select, it autoselect the first possible value.. But that autoselection is just "graphical".
I know this could be fixed by adding a "null" value in the second select, with the option <option value={''} label={'Select a Model'} />. But I'd like to mantain the autoselection when the first select changes.
EDIT: actually, the fix I proposed is not an actual fix: that 'select a Model' options has the value '', but the handleSelectSelectChange is still not triggered, so, while the UI selected value is '', in the state I still have '911'
React.useEffect(()=> {
if (!secondOptionsMemoized.some(x=> x === secondSelectValue)) {
console.log('Second Select Change in useEffect');
setSecondSelectValue(secondOptionsMemoized[0]);
}
}, [secondSelectValue, secondOptionsMemoized]);
You can use useEffect instead of useMemo and another state which set the second options:
import React, { useEffect } from "react";
import "./styles.css";
export default function App() {
const alfaForSecondOptions = ["Giulietta", "Mito"];
const otherForSecondOptions = ["Cayenne", "911"];
const [firstSelectValue, setFirstSelectValue] = React.useState("Porsche");
const [secondSelectValue, setSecondSelectValue] = React.useState("911");
const [secondOptions, setSecondOptions] = React.useState(
otherForSecondOptions
);
useEffect(() => {
console.log(secondSelectValue);
}, [secondOptions]);
useEffect(() => {
if (firstSelectValue === "Alfa") {
setSecondOptions(alfaForSecondOptions);
setSecondSelectValue("Giulietta");
} else {
setSecondOptions(otherForSecondOptions);
setSecondSelectValue("911");
}
}, [firstSelectValue]);
const handleFirstSelectChange = (e) => {
console.log("First Select Change", e.target.value);
setFirstSelectValue(e.target.value);
};
const handleSecondSelectChange = (e) => {
console.log("Second Select Change", e.target.value);
setSecondSelectValue(e.target.value);
};
return (
<div className="App">
<label>
<p>Brand</p>
<select onChange={handleFirstSelectChange} value={firstSelectValue}>
<option value={"Alfa"} label={"Alfa"} />
<option value={"Porsche"} label={"Porsche"} />
</select>
</label>
<label>
<p>Model</p>
<select onChange={handleSecondSelectChange} value={secondSelectValue}>
{secondOptions.map((x) => {
return <option key={x} value={x} label={x} />;
})}
</select>
</label>
</div>
);
}
Related
I am working on a filter that will fetch the data from serviceNow depending on what the user has selected in the drop down menu. In my onChange(), I have a function call that will be filtering the data based on the value returned by onChange(). However, as soon as I put the function call(handleFilter()), the result is correctly fetched and displayed but the drop down menu selection is not updated.
Here is my drop down menu snippet
export default function DropDownComponent(props) {
return (
<div className="DropDownBox">
<Select
defaultValue={props.options[0]}
isSearchable={false}
name="DropDownBox"
options={props.options}
styles={dropDownStyles}
autosize={false}
onChange = {
val => {
props.changeDropdownValue(val);
props.handleFilter(val)
}
}
/>
<div className="caret_down_centered" />
</div>
);
props.handleFilter(val) is the function call that prevents the updated in the drop down. If I remove this, then the drop down selection is updated but the whole functionality is rendered useless because there is no filtering being done.
handleFilter(val) is a parent class method & is simply invoking different API's depending upon what the value of val is which is passed from onChange(). I have no idea why it is interfering with the drop down menu.
I read that we can use a state and update the state everytime there is a change in selection but since I have a functional component, I am not sure how that will go.
I was not sure if handleFilter() code is needed here since it is a long code but if needed, then please let me know. I will post it here.
Try this:
const DropDownComponent = props => {
return (
<select
name="selectFieldName"
value={props.value}
onChange={props.handleChange}
// rest...
>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
)
}
class Parent extends React.Component {
// initial setup
constructor(props) {
super(props)
this.state = {
value: 2 // Set your default value
}
}
handleFilter = (value) => {
// Here you fetch the data using 'value' argument
console.log(value)
}
handleChange = e => {
const value = e.target.value
// update value
this.setState({
value // no need to write 'value' because both have the same name
})
// Call handleFilter passing selected "value"
this.handleFilter(value)
}
render() {
return (
<div>
{/* Passing value and handleChange */}
<DropDownComponent
value={this.state.value}
handleChange={this.handleChange}
/>
<p>Value {this.state.value}</p>
</div>
)
}
}
ReactDOM.render( <Parent / > , document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.min.js"></script>
<div id="root"></div>
I guess your problem is calling handleFilter method in wrong place. Also, you didn't tell how you have set the selected value inside the handleFilter method. See this codeSandbox for example.
I have a react component with a <Select options={myoptions} onChange={this.mychangehandler}/> element from react-select. I have saved some settings using redux that can be accessed e.g. using this.props.myreducer.dropdownvalue(already tested, the values are available when first rendering the component)
Depending on the redux props I want to automatically select one of the options in the dropdown. How can I tell the <Select.../>dropdown to automatically select option 2 if this.props.myreducer.dropdownvalue is equal to 1?
There are countless properties but somehow I found no one that can help me automatically select the proper entry.
Any ideas? Solutions that will automatically trigger onChange as well prefered, but should be no problem if not.
Thanks in advance :)
Regards Christian
edit: Here is the full code:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import Auxiliary from '../../../../../../../hoc/Auxiliary/Auxiliary';
import classes from './Dropdown.module.css';
import Select from 'react-select';
class Dropdown extends Component {
changeSetting = (event) => {
console.log('qid'+this.props.qid)
console.log('event: '+JSON.stringify(event))
if(this.props.certs.certquestions[this.props.qid].red === event.id)
{
this.props.colorchanger("red")
}else if(this.props.certs.certquestions[this.props.qid].yellow === event.id)
{
this.props.colorchanger("yellow")
}else if(this.props.certs.certquestions[this.props.qid].green === event.id)
{
this.props.colorchanger("green")
}
}
render(){
const options = []
const qid = this.props.qid
console.log('qid:'+qid)
const question = this.props.certs.certquestions[qid]
const opt = question.options.split("|")
for(let i = 0;i<opt.length;i++){
let optname = opt[i]
options.push({value:i, label:optname, id:i})
}
let notes = "";
let selectedOption = null;
if(this.props.loadp)
{
let progress = this.props.certs.loadedProgress[this.props.users.users[this.props.users.currentuser].target_id+'_'+this.props.q]
if (typeof progress !== 'undefined')
{
notes = progress.notes
selectedOption = progress.selectedOption
}
}
return(
<Auxiliary>
<h3>{question.title}</h3>
<Select className = {classes.Dropdown} options={options} onChange={this.changeSetting}/ value={selectedOption}> //selectedOption is an Integer
<textarea value = {notes}/>
</Auxiliary>
)
}
}
const mapStateToProps = state => {
return {
certs: state.certs,
users: state.users
};
}
export default connect(mapStateToProps, null)(Dropdown);
the options have different labels and value and id from 0 to 9. I tried to add value={selectedOption} to the Select element, also tried it with the label instead, but it always only shows the "Select..." placeholder and doesn't select the option and trigger the onChange. Any idea what I'm doing wrong?
Solved: I now know what I did wrong, thanks a lot :) I needed to pass an object with value, id and label to value :)
I needed to pass an object like this: {"value":0,"label":"v1","id":0} to value
I'm using Semantic UI React to create a select country dropdown. In order to get values that are passed as attributes to a <div role=option />.
In order to get the selected div I use this:
handleSelectCountry = (e, props) => {
const selected = $('div.item[aria-selected=true]');
const flag = selected.get(0).children[0].className;
console.log(selected);
this.setState({ country: props.value, flag });
};
When logged, it logs the previous selected div as <div role="option" aria-selected="false">Previous selected</div>
However, using the same selector in the Chrome console gives the correct <div role=option aria-selected="true">Current selected</div>.
The DropDown code is (from semantic-ui-react):
<Dropdown
fluid
required
placeholder="Select Country"
search
selection
options={Form.countryList}
value={country}
onChange={this.handleSelectCountry}
/>
The onChange handler is called with its first argument to be an instance of a React SyntheticEvent [1]
The currentTarget can be retrieved from it and the icon tag contained in it queried for.
const onChange = (event, data) => {
const [ icon ] = event.currentTarget.getElementsByTagName('i');
const flag = icon.className;
this.setState({ country: props.value, flag });
}
I'm using react-select in my project and I'm using it within a map like that:
renderItems() {
this.props.items.map(item => (
<Select
id="options"
value={this.state.optionSelected}
onChange={this.onChangeOption}
options={this.showOptions()}
/>
);
}
It show correctly all my options for all my items but now I can not get rid about the select...
Basically when I select an option on a single item, that option is changing for all items...
This is what i did so far:
onChangeOption(e) {
this.setState({ optionSelected: e.value });
}
How can I adjust it to change the option only on the one I wish to change it?
Thanks
You are using the same change handler for all of your select components and then set the same state value for all your select components. To deal with this either you need to separate your select components with a container component that handles their own state and change event or you need to give each select component a unique state value.
Example
renderItems() {
this.props.items.map(item => (
<Select
id="options"
value={this.state.optionSelected[item.id]}
onChange={(event) => this.onChangeOption(event, item.id)}
options={this.showOptions()}
/>
);
}
onChangeOption(event, itemId) {
this.setState((prevState) => {
const prevStateClone = Object.assign({}, prevState);
prevStateClone.optionSelected[itemId] = event.target.value;
return prevStateClone;
});
}
Instead of making optionSelected string variable, make it as array in state.
Now do the following.
renderItems() {
this.props.items.map(item, index => (
<Select
id="options"
value={this.state.optionSelected[index]}
onChange={(selectedValue) => this.onChangeOption(selectedValue, index)}
options={this.showOptions()}
/>
);
}
onChangeOption(selectedValue, index) {
const optionSelected = this.state.optionSelected.slice() // slicing to get new copy of optionSelected instead of referencing to old one which causes mutation
optionSelected[index] = selectedValue
this.setState({optionSelected: optionSelected})
}
What you were doing is using a single variable to hold values of the select box. So if anyone changes, it will reflect all select box
Try cloning the object to a new object or if this optionSelected is a class, you can implement a constructor that clones it for your like:
export class optionSelectedClass {
myfield = '';
constructor(fields){
this.myField = fields.myField;
}
}
or even
export class optionSelectedClass {
myfield = '';
constructor(fields){
for (let f in fields) {
if (!this[f]) {
this[f] = fields[f];
}
}
}
}
I have my select list component rendering my select list:
<form className="pure-form">
<select ref="selectMark" className="mark-selector"
onChange={this.onChange}>{this.getOptions()}
</select>
</form>
I have a method on the component to create the options:
getOptions: function () {
return this.props.renderProps.data.map(function (item) {
return <option key={item.value} value={item.value}>{item.label}</option>;
}.bind(this));
},
My onChange method works fine with the value:
onChange: function(event) {
var newValue = event.nativeEvent.target.value;
this.props.renderProps.onSaveCare(newValue);
this.setState({value: newValue});
this.toggleEdit();
},
Is there a way I can get the option text? This gives me undefined
event.nativeEvent.target.text; //undefined
Something like this should do
var index = event.nativeEvent.target.selectedIndex;
event.nativeEvent.target[index].text
Here is a demo http://jsbin.com/vumune/4/
You can get the option text by replacing this:
event.nativeEvent.target.text;
with this:
event.target.options[event.target.selectedIndex].text
If it's single select, here is more simple way:
e.target.selectedOptions[0].text
This worked for me
const {options, value} = e.target;
console.log(options[value].innerHTML);
Edit: I just realized I was using the "value" field to store the ID of some objects, from 0 to n. I guess a better approach could be the following:
const {options, selectedIndex} = e.target;
console.log(options[selectedIndex].innerHTML);
The text of an option is simply the label property of the corresponding item.
In your case, to retrieve the text of the selected option, you can do:
var selectedItem = this.props.renderProps.data.find(function (item) {
return item.value === event.target.value;
});
selectedItem.label;
Array.prototype.find is part of the ES6 proposal. Underscore or lodash already package it as the _.find method.
Here what i do to retrieve the text from select option in react js.
this.refs.selectMark[this.refs.selectMark.value].text
It's easy using refs.
import the hook
import React, { useContext, useState, useEffect, useRef } from "react";
instantiate it
const yourNewRef= useRef();
Reference it in your element
ref={yourNewRef}
Then you can access it in any event or function by simply calling:
let textSelectOption = yourNewRef.current.options[yourNewRef.current.selectedIndex].text
You can access the value simply by calling
let optionValue = yourNewRef.current.value
refs are great, you can do almost everything you would using jQuery.
Take a look at yourNewRef on console and access all the content via the .current property
2020 and this Worked For Me
My Select Element :
<FormGroup>
{<Select
closeMenuOnSelect={true}
components={animatedComponents}
options={Options}
value = "Ahmedabad"
onChange={this.handleChange}
name = "citySelect"
/>}
</FormGroup>
Call handler :
handleChange = (e) => {
console.log(e.value)
}
change your menu item accordingly
Current / Temporary address
Permanent address
Office / Business Address
and on change event get
onChange = {(e,index) =>
( setAddressChangeType(e.target.value), console.log(index.props.id) )
}
make your dropdown accordingly and get value on change event like
onChange = {(e,index) =>
( setAddressChangeType(e.target.value), console.log(index.props.id) )
}