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
Related
To be able to add the headers dynamically using a React Select component, I overrode the ToolBar of MaterialTable.
The override worked, but now when I select more than 3 checkboxes and try to update the columns in MaterialTable's datamanger, the app terminates/freezes.
(The documentation of MaterialTable didn't help me much with the implementation, so I had to work through the code to update the columns. That's why I'm not sure if I'm doing this correctly. Furthermore this is only a rough first implementation.).
I would appreciate any tips on this. Thanks in advance ^^
The following code is inside the overridden Material-Table ToolBar componente. I simply "copied" the implementation of Material-Table and pasted the React-Select component into the ToolBar render method.
handleChange = (selected) => {
let columns = this.props.dataManager.columns;
for (let index in columns) {
columns[index].hidden = false;
}
for (let index in selected) {
let cIndex = columns.findIndex(object => {
return object.title === selected[index].value;
});
if (cIndex !== -1) {
columns[cIndex].hidden = true;
}
}
this.setState({
optionSelected: selected,
});
this.props.dataManager.setColumns(columns); //?
this.props.onColumnsChanged(columns); //Is this correct?
}
renderSelectHeaders() {
let columns = this.props.columns.map((columns) => ({label: columns.title, value: columns.title}))
return(
<span
class="d-inline-block"
data-toggle="popover"
data-trigger="focus"
data-content="Please selecet columns"
>
<ReactSelect
options={columns}
isMulti
placeholder={"Selected Columns"}
closeMenuOnSelect={false}
hideSelectedOptions={false}
components={{
Option
}}
onChange={this.handleChange}
allowSelectAll={true}
value={this.state.optionSelected}
className={"select"}
/>
</span>);
}
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>
);
}
I have a parent Component which sends a list of data to a List component which in turn sends it to a Label Component to display each data as a label.
I want to be able to focus on the label element when i click on it so that the appropriate style is applied ..
Below is the gist :-
class ContainerComp extends React.Component {
constructor(props) {
super(props);
this.state = {
group: [1, 2, 3]
};
clickHandler = (name, ref) = > {
// I am able to get the DIV as a html element here but calling .focus() on it dosent change the style where as when i explictly add focus using chrome debugger for the element it works.
ref.focus() // not working
}
render() {
return ( <
ListComp group = {
group
}
onClick = {
clickHandler
} >
)
}
}
function ListComp(props) {
const data = props.group.map(... < label onClick = {} > )
return ( <
Label.. >
)
}
function Label(props) {
let ref = createref();
// on focus style for the component is defined in this component
// i am making use of css modules
return ( <
div ref = {
ref
}
onClick = (name, ref) >
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
How can we achieve such a functionality without having to pass a selected prop to the label component ? By default i would select the first element and keep the focus .. or we can make it configurable.
Usually for this I would use Redux and fire off an action which therefore sets the property of the component that needs change, and make a listener that will listen for that specific prop and change style accordingly.
In this situation, id just pass down the event handler to the child component (remember to not call it when you pass it down, so do:
{() => {eventHandler()}}
and then in the child component do:
onClick={this.props.eventHandler(e)}
You will use the event to identify which element triggered it and then apply the class/style/prop to it.
There was some problem with the Ref , I am not quite sure why but i changed it to use the useRef() hook.
Label Component
const elementRef = useRef(null);
return (
<div className={[externalStyle, styles.container].join(' ')} onClick={() => onClickEvent(itemName, elementRef)} ref = {elementRef} tabIndex={1}> // added tabIndex and also changed to useRef
Container Component
clickHandler = (name: string, ref) => {
ref.current.focus(); // volla it worked
}
I tried using the old form of Ref and also useRef() without null previously (el) => (const = el).
It works if some one has some explanation where i went wrong i will be happy to listen as i am able to wrap my head around. may be a nights sleep helped fix it :P
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) )
}