Handling events on dynamically created <select> dropdown options - javascript

Let's say I have a function which returns a dropdown. This function gets called from some parent, where the parent passes in props including a state key, array, and onChange. The dropdown is created dynamically from the items in the array. What I want to happen is, when an option in the dropdown is selected, the parents passed in state key gets updated to the value of whatever was selected. Currently, I am trying to do this by setting an onClick handler per , which doesn't work. Instead, I am met with either no or undefined values (described more below).
Example flow:
Parent passes in aStateKey (actual state key), an array to be used as dropdown values, and an onChange function to be used to update aStateKey
The dropdown menu is created from the passed inarray
A dropdown item is selected
aStateKey (which was passed in as a prop) gets updated via the passed in onChange function.
I understand that the traditional method is to give and onChange handler, but I am having troubles working out how to get the desired described above.
Parent
state = { aStateKey: "" };
someArray = ["test", "another test"];
updateField = (name, value) => {
console.log("Updating field: " + name + " with value: " + value);
}
return(
<div>
<CreateDropdown name="aStateKey" items={this.someArray} onChange={this.updateField} />
</div>
);
CreateDropdown
function CreateDropdown(props) {
const handleClick = event => {
console.log("changed name:" + event.name + "changed value: " + event.value);
props.onChange(event.name, event.value);
};
return (
<div>
<select>
{props.items.map(field => (
<option key={field} value={field} name={props.name} onClick={handleClick}>
{field}
</option>
))}
</select>
</div>
);
}
Console log
Shows nothing! However, if I move the onClick from <option> to <select>, i.e.
return (
<div>
<select onChange={handleClick}>
{props.items.map(field => (
<option key={field} value={field} name={props.name}>
{field}
</option>
))}
</select>
</div>
);
The console shows:
Updating field: undefined with value: undefined.
changed name:undefinedchanged value: undefined
How can I achieve my desired behavior?

your target form this event is select and use onChange and here the updated function you need:
function CreateDropdown(props) {
return (
<div>
<select name={props.name} onChange={e =>
props.onChange(e.target.name, e.target.value);}>
{props.items.map(field => (
<option key={field} value={field}
{field}
</option>
))}
</select>
</div>
);
}

UPDATE #1
Inside handleClick, you are using event.name and event.value to get the target values you want.
instead use event.target.name and event.target.value
Try using onChange instead of onClick in your select element.
It belongs to select not option elements.

Related

Setting value={file} in <option> tag and accessing from an onChange function in React

I am using React. I am trying to get a featured image from a list of files that the user has selected. I am trying to make the user select a number which corresponds to an image file. I have the following:
const handleFeatureImageChange = (event) => {
setFeatureImage(event.target.value);
console.log(event.target.value);
};
return(
{selectedFiles.length > 0 && (
<div>
<select name="is_feature" onChange={handleFeatureImageChange}>
{selectedFiles.map((selectedFile, index) => {
return (
<option key={index} value={selectedFile}>
{index + 1}{console.log(selectedFile)}
</option>
);
})}
</select>
</div>
)}
);
But console.log(event.target.value) inside the handleFeatureImageChange function prints [object File] as a string. But I want the actual file object there. console.log(selectedFile) inside the jsx prints the actual file object. How can I get the file object in the handleFeatureImageChange function ?

How to Implement Multi Select Dropdown in React

I'm looking for a good way to create multi select dropdowns in plain React, without using an additional library.
At present, I’m doing something like this:
<select className='yearlymeeting' multiple={true}>
<option value=''>Yearly Meeting</option>
{
this.state.meeting.yearly_meeting.map((m: Meeting, i: number) => {
return (
<option
value={m.title}
key={i}
selected={this.state.selectedTitles.yearlyMeetingTitles.includes(m.title) ? true : false}>
{m.title}
</option>
);
})
}
</select>
This code "works", but I'm getting this warning:
Warning: Use the `defaultValue` or `value` props on <select> instead of setting `selected` on <option>.
From react docs -
You can pass an array into the value attribute, allowing you to select multiple options in a select tag:
<select multiple={true} value={['B', 'C']}>
I think you just need to pass your selected items array to value prop of select element.
Instead of checking conditions and setting "selected" props in the "option" element, directly set the value in the "select" element. The warning should go away.
<select className='yearlymeeting' multiple={true}
value={this.state.selectedTitles.yearlyMeetingTitles}>
<option value=''>Yearly Meeting</option>
{
this.state.meeting.yearly_meeting.map((m: Meeting, i: number) => {
return (
<option
value={m.title}
key={i}
{m.title}
</option>
);
})
}
</select>

React submit form on select change event

I'm using ant design. I have a form. In that form I have submit button and select dropdown.
when I click submit button it triggers the form submit action.
I need to submit form and get values on select change event.
Code sandbox: https://codesandbox.io/s/xrpzw7wn8q
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log("Received values of form: ", values);
}
});
};
I will try to answer this question in a more general way because it pops in the search results and it may help some people.
To submit a select form on a change event (without a submit button) in React, you have to do two things:
use the "onChange" property to fire a function to send the value.
in this function, you have to trigger the submit action of the form.
To do number 2, in classical javascript you would grab a reference to the form and then use the submit() method. In React, to grab the form, you can use "useRef". The submit() method is then accessible on the "current" property. Here is an implementation:
import { useRef }, React from "react"
export default function SelectOnChange () {
const selectForm = useRef(null)
const handleSubmit = () => {selectForm.current.submit()}
return (
<form ref={selectForm} method="get" onChange={handleSubmit}>
<select name="sort" id="sort">
<option value="relevance">Relevance</option>
<option value="score">Score</option>
</select>
</form>
)
}
You can add a callback to the onchange event of the Ant Design Select component, that could handle the form submit:
<Select
allowClear
onChange={
(value) => {
// your code to submit the form
}
}
>
<Option key={1} value={1}>
something 1
</Option>
<Option key={2} value={2}>
something 2
</Option>
</Select>
BUT from an UX perspective, if the submit button has to be present, it should be it to trigger submitting
You can do something along those lines:
<Select allowClear onSelect={ (val, event) => this.handleSubmit(event) }>
Add the onChange() or onSelect() from ant design select and access the values from the form in the callbacks.
state={
selectValue : "default value",
otherFormItemValue: "default other value"
}
handleSubmit = () => {
serviceCallToSubmitForm(this.state.selectValue, this.state.otherFormItemValue);
}
//In the render() of component
<Select
onChange={
//or onSelect
(value) => {
this.setState({selectValue: value});
this.handleSubmit()
}
}
>
<Option key={1} value={"value 1"}>
something 1
</Option>
<Option key={2} value={"value 2"}>
something 2
</Option>
</Select>
<Button onClick={this.handleSubmit}>Submit</Button>
Hope this helps and is clear enough.
The handleSubmit function is used to access the form values from the state and submit it.
The onChange function call it used to :
1. Store the dropdown value in the state
2. call the handleSubmit function to actually submit in the same action [not recommended UX-wise]
Here is code sandbox.
https://codesandbox.io/s/r00v7x8r7q
Select dropdown item and check console for the respected value.
Getfield decorator doesn't have onchange option, so remove it and add onchange event to select tag instead of getfield decorator.
Check ant docs for available options.
https://ant.design/components/form/
getFieldDecorator(id, options) parameters

React JS: Pass event inside onChange drop down (Ant Design)

I have a drop down in my form (https://ant.design/components/select). In this select drop down I have the onChange to call a function. Inside 'onChange' I want to pass the event as a parameter to my function. The problem is: when the onChange occurs, only the selected value is passed, but I want the entire event.
Here is the code:
export default class MyForm extends Component {
constructor() {
super();
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange = (event) => {
console.log(event); // here I'm receiving only the value selected (1 or 2)
}
render() {
render(
<Form>
<Select onChange={this.handleOnChange}>
<Option value="1">text 1</Option>
<Option value="2">text 2</Option>
</Select>
</Form>
)
}
}
In the console.log() I'm receiving only the selected value. Is there a way to pass the entire event object to the function handleOnChange()?
I found a solution. Just use: onSelect(), passing the value and the event.
handleOnChange = (value, event) => {
...code here
}
render() {
render(
<Form>
<Select onSelect={(value, event) => this.handleOnChange(value, event)}>
<Option value="1">text 1</Option>
<Option value="2">text 2</Option>
</Select>
</Form>
)
}
The Select component that you use is the one that handle the onChange and call your "outer" function.
What you can try is use the synthetic event variable inside your function, it might work:
handleOnChange = (selectedValue) => {
console.log(selectedValue); // The value from the inner component
console.log(event); // usually you have access to this variable
}
Try this, if you dont want to bind in callback in Select onSelect/onChange:
toggleActive = name => event => {
console.log("name: ",name) // prints "name: Active!"
console.log("event: ",event) // event is some data
}
<Select
mode="multiple"
style={{ width: '91%' }}
placeholder="Stuff"
value={this.props.value}
onChange={this.toggleActive("Active!")}
>

React defaultValue for Select with Dynamically Generated Options

Use the defaultValue or value props on instead of setting selected on .
<select defaultValue="react">
<option value="react">React</option>
<option value="angular">Angular</option>
</select>
defaultValue would work with the above select tag. However, it does not seem to work with options generated by loop.
<select defaultValue={selectedOptionId}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
Probably options not fully been set when defaultValue was declared?
I could do manual assigning in componentDidUpdate() and onChange event.
But my question is - Is there any cleaner(better) way to solve it?
Thanks.
This is old, but since answer #1 is related to a controlled Select and the question seems to be related to uncontrolled Select I think is worth to leave some lines for future readers:
The problem is that for uncontrolled components React needs to know what are the options before the first render, since from that moment the defaultValue won't override the current value of the Select. This means that if you render the Select before the options it won't know what to select.
You can solve the problem avoiding the render before the options are available:
const RenderConditionally = ({ options, selected }) => options.length > 0 ? (
<select defaultValue={selected}>
{options.map(item => (
<option key={item.id} value={item.value}>{item.label}</option>
))}
</select>
) : null;
Or without ternary if you desire:
const RenderConditionally = ({ options, selected }) => {
if (options.length === 0) {
return null;
}
return (
<select defaultValue={selected}>
{options.map(item => (
<option key={item.id} value={item.value}>{item.label}</option>
))}
</select>
);
};
For users running into this issue, you can get the desired functionality by using the value prop, instead of defaultValue, e.g.:
<select value={selectedOptionId}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
Most probably you have something wrong with option_id and options arrays structure, or selectedOptionId variable. The way you build your select component is ok.
I've made a fiddle where this code works fine:
render: function() {
let option_id = [0, 1];
let options = [{name: 'a'}, {name: 'b'}];
let selectedOptionId = 1
return (
<select defaultValue={selectedOptionId}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
)
}
The best solution that I could find is to set key attribute the same as defaultValue so it will be a different element.
Aftermaths are not researched by me but I believe it should be okay.

Categories