Prevent React-Select from ever being blank - javascript

We are using React-Select within our forms.
We would like to have a select dropdown that can never be blank (similar to include_blank: false in Rail's SimpleForm).
<Select
simpleValue={true}
id={input.name} {...input} {...inputHtml}
className={inputClass}
name="form-field-name"
value={value}
onChange={this.handleChange}
options={selectOptions}
multi={this.props.multi}
clearable={false}
/>
I am passing in options (which are appearing, and setting clearable to false, but the select field is still able to be blank. Is there a way to prevent the blank from even being an option?

If you ignore setting a selectedOption when you don't get an option to the onChange callback it will work as expected:
Example
class App extends Component {
constructor(props) {
super(props);
const options = [
{ value: "one", label: "One" },
{ value: "two", label: "Two" }
];
this.state = {
options,
selectedOption: options[0]
};
}
handleChange = selectedOption => {
if (selectedOption) {
this.setState({ selectedOption });
}
};
render() {
const { options, selectedOption } = this.state;
return (
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
clearable={false}
/>
);
}
}

Related

Unfocusing dropdown menu sets its state as undefined

I'm making a dropdown menu that allows user to set a state and then see the page corresponding to chosen values.
I isolated my code to fully reproduce the issue both in text and in this [CodeSandbox]
Desired baheviour - Open menu, set state using its componets, close menu and keep the state.
Current behaviour - Open menu, set state using its components, close menu and state is set to undefined.
I track the changes to the state in the console and can clearly see that adding items to filter is seen in the updated state every time. However when I close the menu the state changes to undefined and the state is unsuable for my needs.
How do I change the code so the state persists when the menu is closed?
Thanks in advance for your time!
import React from "react";
import { default as ReactSelect } from "react-select";
import { components } from "react-select";
export default function BettingDeck(props) {
const sportsOptions = [
{ value: "soccer", label: "Soccer" },
{ value: "dota", label: "Dota 2" },
{ value: "tennis", label: "Tennis" },
{ value: "csgo", label: "CS:GO" }
];
const Option = (props) => {
return (
<div>
<components.Option {...props}>
<input
type="checkbox"
checked={props.isSelected}
onChange={() => null}
/>{" "}
<label>{props.label}</label>
</components.Option>
</div>
);
};
const [sportsSelectorState, setSportsSelectorState] = React.useState({
optionSelected: [],
isFocused: true
});
function handleChange(selected) {
setSportsSelectorState(() => {
return { optionSelected: selected };
});
}
console.log(sportsSelectorState.optionSelected);
return (
<>
<div className="betting-deck-container">
<div className="betting-deck-head-container">
<div className="betting-deck-title">Betting Deck</div>
{/* <SportsSelector /> */}
<span
class="d-inline-block"
data-toggle="popover"
data-trigger="focus"
data-content="Please selecet account(s)"
onBlur={() => {
setSportsSelectorState({ isFocused: false });
}}
onFocus={() => {
setSportsSelectorState({ isFocused: true });
}}
style={
sportsSelectorState.isFocused ? { zIndex: 1 } : { zIndex: 0 }
}
>
<ReactSelect
options={sportsOptions}
isMulti
closeMenuOnSelect={false}
hideSelectedOptions={false}
components={{
Option
}}
onChange={handleChange}
allowSelectAll={true}
value={sportsSelectorState.optionSelected}
placeholder="Select sports to filter"
menuPortalTarget={document.body}
classNamePrefix="mySelect"
/>
</span>
</div>
</div>
</>);}
Every time you set the state, you overwrite it with a new object.
So this:
setSportsSelectorState(() => {
return { optionSelected: selected };
});
practically removes isFocused from the object.
And this removes optionSelected:
setSportsSelectorState({ isFocused: true });
So to always preserve the entire object, spread the previous state (object) into the new and only overwrite the relevant property:
// The parameter in the callback function (prev)
// always holds the previous state, or should
// I say the state as it currently is
// before you change it.
setSportsSelectorState((prev) => {
return { ...prev, isFocused: true };
});
// or
setSportsSelectorState((prev) => {
return { ...prev, optionSelected: selected };
});

How can I force the "select" to return to "Choose..." after submit form in React?

How can I force the "select" to return to "Choose..." after submit form?
<select name="activated" className="form-control" as="select" onChange={handleInputChange}>
<option value={values.activated} selected>Choose...</option>
<option value="Activated">Activated</option>
<option value="Deactivated">Deactivated</option>
</select>
I think I have to use the blur or focus method. Some idea?
import Select from 'react-select';
export class Test extends Component {
this.setState = {
selection: 0,
};
onSelectChange = (e) => {
this.setState({ selection: e.value });
}
onSubmit = (e) => {
e.preventDefault();
const { selection } = this.state;
// Post request logic comes here
// Reset Select component to original default state
this.setState({selection: 0});
}
render() {
const options = [
{ label: 'Choose something', value: 0 },
{ label: 'Item 1', value: 1 },
{ label: 'Item 2', value: 2 },
];
return (
<form onSubmit={this.onSubmit}>
<Select
options={options}
value={this.state.selection}
onChange={this.onSelectChange}
/>
//submit button here
</form>
);
Use value prop instead of defaultValue and manage it through state.On Submit, reset that state to initial value i.e 0.
I found a simpler solution than Tayyab's answer:
Adding this code to my handleSubmit function:
document.getElementById("myId").selectedIndex = 0

Blur validation gets triggered before input selection fills the data field - ReactJS

Following is my country selector drop down which pops in as soon as user Focus the input field. I have made a validation onBlur as data cannot be remains empty in the input field. Problem is on selecting the value from the list onBlur fires immediately and then the values gets populated in the dropdown which is causing onBlur validation message to appear on screen.
Let me know how can I manage the situation by removing the onBlur validation once user selects the value from the list.
React Code -
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
blurText: "Field Cannot be left blank",
listData: [],
selectedValue: "",
showList: false,
showBlurText: false
};
}
componentDidMount() {
this.setState({
listData: [
{ id: 1, name: "Australia" },
{ id: 2, name: "Germany" },
{ id: 3, name: "France" }
]
});
}
handleBlur = e => {
if (!e.target.innerHTML) {
this.setState({
showBlurText: true
});
} else {
this.setState({
showBlurText: false
});
}
};
showDataList = () => {
this.setState({
showList: true
});
};
handleSelection = e => {
this.setState({
selectedValue: e.target.innerHTML
});
};
handleChange = e => {
this.setState({ selectedValue: e.target.value });
};
render() {
return (
<div className="App">
<h3>Test Select</h3>
<input
type="text"
name="autosuggest"
value={this.state.selectedValue}
onFocus={this.showDataList}
onBlur={this.handleBlur}
onChange={this.handleChange}
/>
{this.state.showList && (
<ul>
{this.state.listData.map(x => {
return (
<li key={x.id} onClick={this.handleSelection}>
{x.name}
</li>
);
})}
</ul>
)}
<hr />
{this.state.showBlurText && (
<p style={{ color: "red" }}>{this.state.blurText}</p>
)}
</div>
);
}
}
Working Codesandbox with the scenario - https://codesandbox.io/s/charming-brook-h7kin
I have a solution, you can check on this link:
Codesandbox: Click catcher on blur
The issue is you put the onBlur prop to the input element, so when you click an item in the ul element, the onBlur gets triggered.
So what I did is to create a simple click-catcher so when you click outside of input or ul, it will be triggered.
Hope this helps.
Instead of using onBlur you can check validations on onChange event, check the working sandbox here
some tips below that will make your code clean:
Instead of using if-else while validating emptyefields you can directly use !(negate operator) in your handleBlur function.
handleBlur = e => {
this.setState({
showBlurText: !e.target.innerHTML
})
}

getting a controlled input warning but i am not sure what is wrong

I am writing a form in react (which I am new to), and that form opens after I click a menu item that will pass the selected item id. The first loading is fine, but when I click on one of the input and type something, I get:
A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.
I am not sure how to fix that, as the places I read were telling me that my component would give me that message if I am initializing it with undefined, which I don't think I am in this case.
class EditMenu extends React.Component {
constructor(props) {
super(props);
console.log('props constructor:', props);
this.state = {
item: {}
};
this.itemTitleName = 'name';
this.itemTitleDescription = 'description';
this.itemTitletag = 'tag';
}
componentWillMount() {
console.log('will mount');
let itemId = this.props.selectedItem;
let item = this.getitemItem(itemId);
this.setState({ item: item });
}
getitemItem(itemId) {
const itemsInfo = [
{
id: 44,
title: 'title1',
description: 'desc1',
tag:''
},
{
id: 11,
title: 'title2',
description: 'desc2',
tag:''
},
{
id: 222,
title: 'tiotle3',
description: 'desc3',
tag:''
},
];
let item = _.find(itemsInfo, { id: itemId });
return item;
}
componentWillReceiveProps(nextProps) {
console.log('received props!')
const nextId = nextProps.selectedItem;
if (nextId !== this.state.item.id) {
this.setState({ item: this.getitemItem(nextId) });
}
}
handleInputChange = (event) => {
console.log('input change ');
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
console.log(name);
this.setState({
item: {
[name]: value
}
});
}
render() {
return (
<div className="form-container">
<form onSubmit={this.handleSubmit} >
<TextField
id="item-name"
name={this.itemTitleName}
label="item Name"
margin="normal"
onChange={this.handleInputChange}
value={this.state.item.title}
/>
<br />
<TextField
id="item-desc"
name={this.itemTitleDescription}
label="item Description"
margin="normal"
onChange={this.handleInputChange}
value={this.state.item.description}
/>
<br />
<TextField
className="tag-field-container"
name={this.itemTitletag}
label="tag"
type="number"
hinttext="item tag" />
<br /><br />
Photos:
<br /><br />
<Button variant="raised" onClick={this.handleSaveButtonClick} className="button-margin">
Save
</Button>
</form>
</div>
);
}
}
the places I read were telling me that my component would give me that
message if I am initializing it with undefined, which I don't think I
am in this case.
Actually you are :)))
your state is an empty object at the beginning:
this.state = {
item: {}
};
Which means:
this.state.item.description
this.state.item.title
...are undefined. And that's what you pass to your controls as a value - undefined.
<TextField
...
value={this.state.item.title}
/>
<br />
<TextField
...
value={this.state.item.description}
/>
Try to set initial value:
this.state = {
item: {
description: '',
title: '',
}
};
Forms work differently in React, because forms keep some internal state. The documentation provides a good run down

React - how to pass multiple input values to child in on change event when values have different types (is not always e.target.value)?

I have multiple input fields and 1 react-select dropdown field. I created a method in my parent component that sets the state with the values from the input, passes it down to the child which should call the method. My problem is that react-select doesn't take the value but an object like this:
{value: 'xy', name:'x', label: 'y'}
so normally my function in my onChange event handler would look like this (when passing multiple values):
in parent:
testing(e) {
this.setState({
[e.target.name]: e.target.value
})
}
in child:
<input type="text" name="maxfare" onChange={this.onChange}/>
...
onChange(e){
var value = [e.target.name] = e.target.value;
this.props.onChange(value);
}
...
However, while my input fields take:
e.target.value
my select dropdown takes entire 'e' - not e.target.value. I tried to pass my onChange function in child component 2 arguments, calling my method in parent with 2, but that doesn't to work. Any help would be great! My code is below (the relevant parts- if I forgot something that you think is important, please let me know). Ps. I thought about having 2 onChange functions, passing once my value for select dropdown and a second one doing the rest, but then I would need to pass 2 onChange methods to the child and I believe thats not possible in react?! Thanks!!:
Parent:
...
onChangeT(selectValue, value) {
this.setState({
origin: selectValue,
maxfare: value
...
})
}
render(){
....
<Parent cities={this.state.citiesToSelect} origin={this.state.origin} maxfare={this.state.maxfare} onChange={this.onChangeT}/>
...
}
Child:
....
onChangeC(e){
var value = [e.target.name] = e.target.value;
this.props.onChange(e, value);
console.log("name", name)
}
....
<Select
onChange={this.onChangeC}
labelKey='name'
value={this.props.origin}
options={this.props.cities}
/>
<input type="text" name="maxfare" onChange={this.onChangeC}/>
We want to be able to do this in the parent
onChange = (name, value) => {
this.setState({[name]: value});
}
We fix the "wiring" of the children onChange to do exactly that, raise an onChange with a name and a value. Wrap react-select and provide a consistent interface to the parent.
Form example
import * as React from 'react';
import Input from './Input';
import Select from './Select';
export default class Form extends React.Component {
state = {
input: '',
select: '',
options: ['A', 'B', 'C']
};
onChange = (name: string, value: string) => {
this.setState({[name]: value});
}
render() {
return (
<form>
<Input
label="Surname"
name={'input'}
value={this.state.input}
onChange={this.onChange}
/>
<Select
label="Grade"
name={'select'}
value={this.state.select}
options={this.state.options}
onChange={this.onChange}
/>
</form>
);
}
}
Input example
import * as React from 'react';
export default class Input extends React.Component {
onChange = (e) => {
const {onChange, name} = this.props;
if (onChange) {
onChange(name, e.currentTarget.value);
}
}
render() {
return (
<div>
<label>{this.props.label}</label>
<input
type="text"
name={this.props.name}
value={this.props.value}
onChange={this.onChange}
/>
</div>
);
}
}
And a DOM native <Select /> example
import * as React from 'react';
export default class Select extends React.Component {
onChange = (e) => {
const {onChange, name} = this.props;
if (onChange) {
onChange(name, e.currentTarget.value);
}
}
render() {
return (
<div>
<label>{this.props.label}</label>
<select
name={this.props.name}
value={this.props.value}
onChange={this.onChange}
>
{this.props.options.map(o => <option key={o}>{o}</option>)}
</select>
</div>
);
}
}
The fact that react-select doesn't return a native event nor a similar object shape of a native event, is forcing you to normalize the shape of the object that returned from it. You can do that by wrapping the Select component of react-select with your own component and returning a custom object for your use-case.
In this example we are trying to normalize the behavior of our onChange event both for inputs and Select. We will first check if the object that returned is having a target key, if it does we know that this is a native event that we are handling and we will set the state according to the name of the input and its value (exactly how you did it in your example).
If we don't have a target key, then we may handle a different kind of event.
We will check if we get a selectedValue key (just a convention between yourself, you can change the key as you like), then we will set the state by its name and selectedValue that we received.
This will only work if you will pass the name upwards of course.
So the object that you need to return from the custom Select component should look something like this:
{name: this.props.name, selectedValue }
// where selectedValue is the object received from the real Select component
Here is a running example:
const options = [
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' },
]
const moreOptions = [
{ value: 'mike', label: 'johnson' },
{ value: 'lynda', label: 'bog' },
]
class MySelect extends React.Component {
handleChange = selectedValue => {
const { name, onChange } = this.props;
onChange({ name, selectedValue });
}
render() {
const { options, value, ...rest } = this.props;
return (
<Select
{...rest}
value={value}
onChange={this.handleChange}
options={options}
/>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
option1: '',
option2: '',
value1: 1,
value2: '',
value3: 3,
}
}
handleChange = e => {
let nextState;
if (e.target) {
const { name, value } = e.target;
nextState = { [name]: value };
} else if (e.selectedValue) {
const { name, selectedValue } = e;
nextState = { [name]: selectedValue };
}
this.setState(nextState);
}
render() {
const { value1, value2, value3, option1, option2 } = this.state;
return (
<div>
<MySelect
value={option1.value}
onChange={this.handleChange}
options={options}
name="option1"
/>
<div>
<span>input1 </span>
<input value={value1} name="value1" onChange={this.handleChange} />
</div>
<div>
<span>input2 </span>
<input value={value2} name="value2" onChange={this.handleChange} />
</div>
<div>
<span>input3 </span>
<input value={value3} name="value3" onChange={this.handleChange} />
</div>
<MySelect
value={option2.value}
onChange={this.handleChange}
options={moreOptions}
name="option2"
/>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/prop-types#15.5.10/prop-types.js"></script>
<script src="https://unpkg.com/classnames#2.2.5/index.js"></script>
<script src="https://unpkg.com/react-input-autosize#2.0.0/dist/react-input-autosize.js"></script>
<script src="https://unpkg.com/react-select/dist/react-select.js"></script>
<link rel="stylesheet" href="https://unpkg.com/react-select/dist/react-select.css">
<div id="root"></div>

Categories

Resources