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 });
}
Related
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'm trying to test a selected item on a list of items, which is handled on a click event by finding a selected class added to it.
My template:
<div class="mycomp" v-for="(list, i) in listItem" :key="list.id" :class="{ selected: i === selectedlist}">
<button class="md-primary md-raised selectOption" v-if="i !== selectedList" #click="selectItem(list, i)">Select</button>
</div>
Test case:
test('highlight the selected item', () => {
const mountFunction = options => {
return mount(FlightDetails, {
localVue,
...options
})
}
const wrapper = mountFunction()
wrapper.findAll('.selectOption').at(0).trigger('click')
const flightCard = wrapper.findAll('.office-flightDetails').at(0).classes()
expect(flightCard).toContain('selected')
})
Here, I'm triggering a click event for the first button in the list, and expecting class to be added for the first wrapper of the list. But it is not finding the class:
expect(received).toContain(expected) // indexOf
Expected value: "selected"
Received array: ["listItems"]
In jQuery or JavaScript, I can find the index using eq. Here, I used at is it correct?
I'm inferring the button-click is supposed to cause the selected class to be applied to an element in the .office-flightDetails group (not shown in question).
That class won't be applied until the next rendering tick, so you have to await the trigger('click') call:
test('highlight the selected item', async () => {
//... 👆
👇
await wrapper.findAll('.selectOption').at(0).trigger('click')
})
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 dropdown where and i run a function onChange. After onChange i am filtering the current React state. This works, and i am able to filter on a unique value. However, i am not able to reset the state to the previous original state on change after switching to another dropdown select item.
handleStateOnChange = e => {
let selectedWerkgever = e.target.value;
const list = this.state.opleidingen.filter(({ opleiding_werkgever }) =>
selectedWerkgever.includes(opleiding_werkgever)
);
this.setState({
opleidingen: list,
isResetButtonActive: true,
});
console.log('changed');
};
I am filtering on everything inside the array that includes "opleiding_werkgever". But how can i first revert back on change, and re-filter again?
Switching to another dropdown on the same component or in the components sharing same state does not automatically reset the state. Your first call on handleStateChange filters the state and it will remain so until that component is unmounted. You could decide to retain the original opleidingen and then use it to reset the opleidingen when needed.
{
opleidingen: list,
isResetButtonActive: true,
originalOpleidingen : list
}
I would store selectedWerkgever in state and use that to filter your drop down elements. Keep the original list intact.
So simplify your handleStateOnChange:
handleStateOnChange = e => {
this.setState({
selectedWerkgever: e.target.value,
isResetButtonActive: true
});
};
And use this to filter your dropdown options:
<select>
{this.state.opleidingen.filter(({ opleiding_werkgever }) =>
this.state.selectedWerkgever.includes(opleiding_werkgever)).map(item=>
<Option value={item} />}>)
}
...
Trying to build a react component where I need to control checked status of checboxes and select options when change event occurs. But I don't know how it is possible to get value of the checked checkbox(es) and set the state.
We're using custom data-binding. On page load, we're assigning selected value of the select, with jQuery.
Programmatically changing value of the select must update matching check-boxes.
When user checks/unchecks a checkbox, corresponding value must be toggled on the select.
With jQuery I would loop trough check-boxes and build array with checked values then assign this value to the select on checkbox change. And when select change event is triggered, I would uncheck all check-boxes and check the ones matching selected items.
This is my simplified code.
state = {
items: [
{Key: 1, Value: "A"},
{Key: 29, Value: "Z"}
],
selected: [1, 29]
}
function onSelectChange(){
// Update checked checkboxes
}
function onCheckboxChange(){
// Update selected options
}
<div>
<select multiple onChange={onSelectChange} className="hidden">
{this.state.items.map((item, i) =>
<option value={item.Key}>{item.Value}</option>
)}
</select>
<div className="checkboxes">
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange} />
)}
</div>
</div>
You would use this.setState({}) inside the event handler to update the state of a component in React. This triggers a rerender in React which allows you to query the the updated state (this.state.selected).
Be advised that this.setState() expects an immutable object, so you should never change the previous, but always set a new state object!
Answer to comment:
For selectItem:
onSelectChange = event => this.setState({selected:event.target.value})
and for checkboxes (note the prevState):
onCheckboxChange = item => event => this.setState(({selected,...prevState})=> ({
...prevState,
selected: event.target.checked? selected.concat(item): selected.filter(it=> it!== item)
}))
and usage:
{this.state.items.map((item, i) =>
<input
type="checkbox"
key={i}
checked={this.state.selected.indexOf(item.Key) >= 0}
onChange={onCheckboxChange(item)} />
)}
This has the downside that it will create a new function on each rerender, so it's better to create a custom CheckboxItem and pass the item to it and use a handleClick.
onChange function give event where you could check whether the select box is being checked or not using this you can update your state accordingly.
function onCheckboxChange(e){
console.log("checked", e.target.checked);
// Then, on the basis of boolean you can update your state
}