Use enum in react select option - reactjs

I have this enum:
const SPORTS = {
FOOTBALL: 0,
GOLF: 1,
TENNIS: 2
};
I'm trying to use those values in a react select dropdown using lodash's forIn
<select onChange={(event) => this.update(event, sport.id)}>
{_.forIn(SPORTS, (key, value) => {
{
<option value={key}>{value}</option>;
}
})}
</select>;
But am getting the error:
Invariant Violation: Objects are not valid as a React child
EDIT:
Changing to _.map works but am now seeing another issue.
Before I was manually adding all the options like which worked fine:
<option value={0}>FOOTBALL</option>;
<option value={1}>GOLF</option>;
<option value={2}>TENNIS</option>;
Now using the map I get this error:
Each child in an array or iterator should have a unique "key" prop
Now when my select boxes render they are empty but I'm not sure why.

The _.forIn() function returns the iterated object, use _.map() instead:
<select onChange={(event) => this.update(event, sport.id)}>
{_.map(SPORTS, (value, key) => (
<option value={key} key={key}>{value}</option>;
))}
</select>;

Related

How to update div in dom with react

I have this select in a react component
<Select defaultValue="" style={{ width: 200 }} onChange={handleChange}>
<OptGroup label={correctGroup[0].Tag}>
<Option value={correctGroup[0].Text}>{correctGroup[0].Text}</Option>
<Option value={correctGroup[1].Text}>{correctGroup[1].Text}</Option>
<Option value={correctGroup[2].Text}>{correctGroup[2].Text}</Option>
</OptGroup>
<OptGroup label={wordForSelect[0].Tag}>
<Option value={wordForSelect[0].Text}>{wordForSelect[0].Text}</Option>
<Option value={wordForSelect[1].Text}>{wordForSelect[1].Text}</Option>
<Option value={wordForSelect[2].Text}>{wordForSelect[2].Text}</Option>
</OptGroup>
</Select>
Once it gets rendered it's a bunch of divs. I should also mention I'm using antd. When a user selects an option the onChange above is fired and I can see the text that was chosen.
Now based on the text I either want to make the background green or red.
So in the onchange do I use the old school document.getElementByClass to change the background. The selected item gets placed as text in an element that has a class name of "ant-select-selection-item". But since this is generated dynamically by antd I can't use useRef...
So how can I add and remove a class name from a div with the class name "ant-select-selection-item" ?
const handleChange = (value: string, defaultOptionType: any) => {
//??
};
can't you use the style prop of the option?
And you can store the style in the state. Something like this.
https://codesandbox.io/s/select-with-search-field-antd-4-22-8-forked-yt1j6e?file=/demo.js:949-954
I'm not sure if I understood the question. But one solution could be to capture the option value and then filter over all the HTML select options to find the selected option and then add a class to it.
const handleChange = (value) => {
// Get the selected option value
const selectedValue = value.target.value || null;
// Check taht there is a value
if (selectedValue) {
// Find the selected option element by the selected value
const currentOption = [...value.target.options].filter((o) => o.value === selectedValue)
// If there was a match add a class to the option
if (currentOption.length > 0) {
currentOption[0].classList.add("red");
}
}
};

Get value of loop options in React

I am looping through an array of objects that return option inside a select tag. As far as I know, I can't attach event handler on the option tag, so I attached onChange handler on select tag. I can get the value of option tag by default, but as I am looping array of objects, I want to get some other values of the object as well. How do I achieve that?.
Here's the snippet of what I am doing,
<select onChange={handleChange}>
{data.map(item => {
return (
<option key={item.id} value={item.name}>
{item.name}
</option>
);
})}
</select>
How do I get for example item.age and item.address when onChange get invoked ?
I find the solution, I set the value of option tag as an object, stringify it using JSON.stringify() so I can get the whole object, and then just read it with JSON.parse()
<select onChange={handleChange}>
{data.map(item => {
return (
<option key={item.id} value={JSON.stringify(item)}>
{item.name}
</option>
);
})}
</select>

Error Rendering a React Element

I want to make a selection from a drop-down, query movies based on the selected value, map through and render them inside a react component but I got this error
A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
Here's the code:
...
constructor(props) {
super(props)
this.state = { description: '', list: [] }
}
handleList() {
axios.get('https://facebook.github.io/react-native/movies.json')
.then(resp => this.setState({...this.state, list: resp.data})) // Trás a lista atualizada e zera a descrição
console.log(this.state.list)
}
render() {
const loop = this.state.list.map((list) => {
return(
<Grid cols='2'>
<select className="form-control" key={i}>
<option value="teste" onClick={() => this.handleList()}>{list.description}</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</Grid>
)
});
}
There is a couple of issues with this code example.
You are not returning anything inside the render() function. (This is the main issue you are having, or at least that React is crying about)
You are, well at least initially, mapping over an empty array and
The callback of this map you are doing things like list.description even though there exists no list and no description inside of list during the initial render.
You are using the onClick for the options instead of onChange
The endpoint you are requesting returns an object and you try to map through it. This should error because map is not defined as a function of an object.
I have managed to fix the issues I have mentioned including the one you are currently having. To save energy, typing the long explanation and reason that went into fixing your issue, have a look at this demo. It contains the exact same code you have posted, but of course, fixed/working.
I think onclick event is not suitable to use in option. Better use it on select
` <select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
`
Sicne you are rendering a list of forms to reuse the same function, I always use a closure
handleChange = (id) = () => axios
.get(`$URL/id`)
.then(r => r.data)
.then(this.setState)
render() {
return (
list.map(item => <select
key={item.id}
onChange={this.handleChange(item.id)}
>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>))}

flow type for an array of option elements from react

I have a function that returns an array of react <option> elements:
function mapOptionToValues(
element: any,
valueKey: ?string,
optionKey: ?string
): React$Element<*> {
if (!valueKey && !optionKey) {
return (
<option key={element}>
{element}
</option>
);
}
return (
<option value={element[valueKey]} key={element[valueKey]}>
{element[optionKey]}
</option>
);
}
But I get this error:
<option value={element[valueKey]} key={element[valueKey]}>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ React element `option`
24: <option value={element[valueKey]} key={element[valueKey]}>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ property `value`. Property not found in
17: <option key={element}>
^^^^^^^^^^^^^^^^^^^^^^ props of React element `option`
The problem is that React.Element<*> tries to infer the exact type of the component, including its Props, but the two possible returns of your function have different Props.
If you remove the React.Element<*> type from your code, flow will infer it for you as a more general JSX.Element, that will accept both cases.

Warning: Use the 'defaultValue' or 'value' props on <select> instead of setting 'selected' on <option>

SCENARIO A user has a dropdown and he selects an option. I want to display that dropdown and make that option a default value which was selected by that user last time.
I am using selected attribute on option but React generates a warning asking me to use default value on select.
For e.g.
render: function() {
let option_id = [0, 1];
let options = [{name: 'a'}, {name: 'b'}];
let selectedOptionId = 0
return (
<select defaultValue={selectedOptionId}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
)
}
});
Problem is that I don't know the selectedOptionId as the selected option could be any option. How would I find the defaultValue ?
React uses value instead of selected for consistency across the form components. You can use defaultValue to set an initial value. If you're controlling the value, you should set value as well. If not, do not set value and instead handle the onChange event to react to user action.
Note that value and defaultValue should match the value of the option.
In an instance where you want to set a placeholder and not have a default value be selected, you can use this option.
<select defaultValue={'DEFAULT'} >
<option value="DEFAULT" disabled>Choose a salutation ...</option>
<option value="1">Mr</option>
<option value="2">Mrs</option>
<option value="3">Ms</option>
<option value="4">Miss</option>
<option value="5">Dr</option>
</select>
Here the user is forced to pick an option!
EDIT
If this is a controlled component
In this case unfortunately you will have to use both defaultValue and value violating React a bit. This is because react by semantics does not allow setting a disabled value as active.
function TheSelectComponent(props){
let currentValue = props.curentValue || "DEFAULT";
return(
<select value={currentValue} defaultValue={'DEFAULT'} onChange={props.onChange}>
<option value="DEFAULT" disabled>Choose a salutation ...</option>
<option value="1">Mr</option>
<option value="2">Mrs</option>
<option value="3">Ms</option>
<option value="4">Miss</option>
<option value="5">Dr</option>
</select>
)
}
What you could do is have the selected attribute on the <select> tag be an attribute of this.state that you set in the constructor. That way, the initial value you set (the default) and when the dropdown changes you need to change your state.
constructor(){
this.state = {
selectedId: selectedOptionId
}
}
dropdownChanged(e){
this.setState({selectedId: e.target.value});
}
render(){
return(
<select value={this.selectedId} onChange={this.dropdownChanged.bind(this)}>
{option_id.map(id =>
<option key={id} value={id}>{options[id].name}</option>
)}
</select>
);
}
Thank you all for this thread! My colleague and I just discovered that the default_value property is a Constant, not a Variable.
In other React forms I've built, the default value for a Select was preset in an associated Context. So the first time the Select was rendered, default_value was set to the correct value.
But in my latest React form (a small modal), I'm passing the values for the form as props and then using a useEffect to populate the associated Context. So the FIRST time the Select is rendered, default_value is set to null. Then when the Context is populated and the Select is supposed to be re-rendered, default_value cannot be changed and thus the initial default value is not set.
The solution was ultimately simple: Use the value property instead. But figuring out why default_value didn't work like it did with my other forms took some time.
I'm posting this to help others in the community.
Use defaultValue and onChange like this
const [myValue, setMyValue] = useState('');
<select onChange={(e) => setMyValue(e.target.value)} defaultValue={props.myprop}>
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</select>
Example
https://codesandbox.io/s/priceless-lamarr-fetpr?file=/src/App.js
With Hooks and useState
Use defaultValue to select the default value.
const statusOptions = [
{ value: 1, label: 'Publish' },
{ value: 0, label: 'Unpublish' }
];
const [statusValue, setStatusValue] = useState('');
const handleStatusChange = e => {
setStatusValue(e.value);
}
return(
<>
<Select options={statusOptions}
defaultValue={[{ value: published, label: published == 1 ? 'Publish' : 'Unpublish' }]}
onChange={handleStatusChange}
value={statusOptions.find(obj => obj.value === statusValue)} required />
</>
)
A simple solution to set the value without using selected in option,
follow these steps:
Default the value to 0 [which I am not considering it as a value to be in use] to x-column
Refer the image
4 steps
set the "IntialValue" of the x-column : 0 to the
use the same string used in your [selected] and set it here with the if condition and when found, mark the "values.x-column = 0"
Remove the "selected" and other items set [if any] in tag
4.finally set this condition
now navigate , the value in the column is defaulted to the one which is marked in step-2 / that you wanted to be set in selected
I have tried and its working for me
A small correction.. line 45 in image.. read the column as column_value_id
I tried different options to avoid console errors the following option worked with selected option without errors in console:
{const [myValue, setMyValue] = useState('');
const changeHandler = (e) => {
setMyValue(e.target.value);
};
<select id="select" value={myValue} onChange={changeHandler}>
{!myValue&& <option value="">choise from list</option>}
{elementsArr.map((el) => {
return (
<option key={el.id} value={el.name}>
{el.name}</option>
);
})
</select>}

Resources