React-select default behavior is to have the menu pop open when the input value is empty. I want to modify this behavior so that when the input is empty, whether before a user has typed anything or the user has backspaced to get to the empty state, the menu will be closed.
I could not find any prop that enables this behavior, so I thought to do it programmatically, by calling some function that closes the menu in onInputChange. Something like:
onInputChange={(inputValue) => {
this.setState({
inputValue,
});
this.selectRef.closeMenu();
}}
I tried using blur() on the Select ref but it just blurred the input without closing the menu, definitely not the behavior I'm looking for.
Is there a prop or function that's exposed that can fulfill my needs?
You can set the menuIsOpen prop onInputChange like this:
handleInputChange = input => {
this.setState({ open: !!input });
}
<Select
onInputChange={this.handleInputChange}
menuIsOpen={this.state.open}
/>
I kept the open state separately
const [open, setOpen] = useState(false);
<Select
menuIsOpen={open}
onMenuOpen={() => setOpen(true)}
onMenuClose={() => setOpen(false)}
/>
and set false when I want to close it. In this case, like this
onInputChange={(text) => {
if (text === '') {
setOpen(false);
}
}}
Related
I'm working on a form that initially only shows one input field and when it is focused, it shows other inputs and the submit button.
I also want to hide all those extra fields if the form loses focus while they are empty. And this is the part that I'm not being able to implement.
This is my code: I use a controlled form and a state to handle focus.
const FoldableForm = () => {
const [formState, setFormState] = useState(defaultFormState);
const [hasFocus, setFocus] = useState(false);
const handleOnBlur = () => {
if (!formState.message.trim() && !formState.other_input.trim()) {
setFocus(false);
}
};
return (
<form
onFocus={() => setFocus(true)}
onBlur={handleOnBlur}
>
<textarea
name="message"
onChange={(e) => setFormState({ ...formState, message: e.target.value })}
/>
{hasFocus && (
<>
<input
type="text" name="other_input"
onChange={(e) => setFormState({ ...formState, message: e.target.other_input })}
/>
<button type="button">Post comment</button>
</>
)}
</form>
);
}
Currently, if I type something in the text area, setFocus(false) is never invoked, so it works as intended.
Otherwise, if I leave it empty and click on the other input field, the handleOnBlur function is called, it sets focus to false, so the form is 'minimized'.
This is expected because the blur event (from the textarea) is triggered before the focus event (from the new input field). So I tried to use setTimeout to check, after a fraction of a second if the focus event had already occurred.
To do so, I used a second state (shouldShow) that is updated in a setTimeout inside the handleOnBlue function.
setTimeout(() => {
if(!hasFocus) {
setShouldShow(false); // this should cause the form to minimize
}
}, 100);
However, according to the react lifecycle, the value of hasFocus that is passed to the setTimeout function is at the invocation time, not at execution. So setTimeout here is useless.
I also tried to use references, but I couldn't make it work.
In your case i think that the usage of the shouldShow state is redundant and you can also avoid using a timeout which may lead to bugs.
You can take advantage of the FocusEvent.relatedTarget attribute and prevent hiding the extra fields when blur from an input and focus to another happens simultaneously.
The handleOnBlur function should look like this:
const handleOnBlur = (e) => {
if (e.relatedTarget && e.relatedTarget.name === "other_input") return;
if (!formState.message.trim() && !formState.other_input.trim()) {
setFocus(false);
}
};
You can find a working example in this code sandbox.
The problem with this approach is that if you have multiple fields appearing you need to check if any of those is focused like below:
["other_input", "another_input"].includes(e.relatedTarget.name)
This behavior is because of closures in JavaScript. The value of hasFocus is not the value of the variable at the moment your callback inside setTimeout is executed. It's the value when the onBlur callback is executed.
One solution would be to use functional updates.
Define a state which holds both hasFocus and shouldShow inside:
const [state, setState] = useState({ hasFocus: false, shouldShow: false });
When you try to access the previous state using functional updates, you get the most recent value:
setTimeout(() => {
setState((state) => {
if (!state.hasFocus) {
return { ...state, shouldShow: false };
}
return state;
});
}, 100);
codesandbox
Another solution would be to debounce a function which sets the hasFocus state to false, which imo is way better.
I am trying to handle with read-only on my input component. So basically, I have an input component and as default, it comes with read-only. What I am trying to do is when it is clicked inside of the input field, read-only comes false and it is editable. But I want read-only true again, only when it is clicked to the outside of the input box.
So here is my component:
const InputComponent = () => {
const [disabled, setDisabled] = useState(true);
function handleClick() {
if(disabled == true) {
setDisabled(false);
}
else {
//TODO: click outside the set readonly
}
}
return (
<Form.Control type="number" readOnly={disabled} onClick={handleClick}/>
);
};
So my logic is quite simple when, disabled is true that means read-only is true so when it is clicked inside, disabled turns false and it is being editable. But I couldnt do the rest. So I dont know how to make disabled false again when it is clicked outside.
Thanks for your help. And I am open more idea.
You can use onBlur to do that:
<Form.Control onBlur={() => {setDisabled(true)}} />
I have some input boxes, select drop down, and date picker that are not being reset like I thought they would. Let's start with the input box and select drop down. Here is their code:
<StyledInput
onChange={handleSearchChange}
value={searchText}
placeholder={"Search..."}
/>
<SelectDropDown
isClearable={false}
isSearchable={false}
strict={true}
allowInvalid={false}
getOptionLabel={opt => opt.name}
getOptionValue={opt => opt.value}
value={searchOptions.find(opt => opt.value === searchCol)}
options={searchOptions}
onChange={value => {
handleColSelect(value);
}}
/>
Notice the handleSearchChange and handleColSelect:
const handleColSelect = value => {
setSearchCol(value.value);
};
const handleSearchChange = event => {
setSearchText(event.target.value);
};
I have a button that I want to reset these to their initial states (i.e. get rid of text in the input box, and reset the SelectDropDown).
I've tried doing so:
const clearFilters = () => {
setSearchText(undefined);
setStartDate(undefined);
setEndDate(undefined);
setSearchCol(undefined);
};
And in my button, simply this:
<GreenOutlineButton
onClick={() => {
clearFilters();
}}
>
Clear Filters
</GreenOutlineButton>
There is another part to this to remove the queryParams that are set, and my button does so- however, it does not take away the text or reset the dropdown. Any ideas here?
From the look of it, setSearchText("") should work fine. For the SelectDropDown, you need to add one more entry to the options which will be your default entry. You can do this as follows:
options={[...searchOptions,{label:"Placeholder", value: null}]}
Then, inside clearFilters, use:
setSearchCol(null)
This should give you the desired result. Let me know if this works.
Is there some way to instruct react-select to select an option on menu close or select blur, but only if it is the one created (not from default list)?
Context:
I have a list of e-mail addresses and want to allow user to select from the list or type new e-mail address and then hit Submit button. I do the select part with react-select's Creatable component and it works.
import CreatableSelect from 'react-select/creatable';
<CreatableSelect
options={options}
isMulti={true}
isSearchable={true}
name={'emailAddresses'}
hideSelectedOptions={true}
isValidNewOption={(inputValue) => validateEmail(inputValue)}
/>
But what happens to my users is that they type new e-mail address, do not understand they need to click the newly created option in dropdown menu and directly hit the Submit button of the form. Thus the menu closes because select's focus is stolen and form is submitted with no e-mail address selected.
I look for a way how can I select the created option before the menu is closed and the typed option disappears.
You can keep track of the inputValue and add the inputValue as a new option when the onMenuClose and onBlur callbacks are triggered.
Keep in mind that both onBlur and onMenuClose will fire if you click anywhere outside of the select area. onMenuClose can also fire alone without onBlur if you press Esc key so you will need to write additional logic to handle that extra edge case.
function MySelect() {
const [value, setValue] = React.useState([]);
const [inputValue, setInputValue] = React.useState("");
const isInputPreviouslyBlurred = React.useRef(false);
const createOptionFromInputValue = () => {
if (!inputValue) return;
setValue((v) => {
return [...(v ? v : []), { label: inputValue, value: inputValue }];
});
};
const onInputBlur = () => {
isInputPreviouslyBlurred.current = true;
createOptionFromInputValue();
};
const onMenuClose = () => {
if (!isInputPreviouslyBlurred.current) {
createOptionFromInputValue();
}
else {} // option's already been created from the input blur event. Skip.
isInputPreviouslyBlurred.current = false;
};
return (
<CreatableSelect
isMulti
value={value}
onChange={setValue}
inputValue={inputValue}
onInputChange={setInputValue}
options={options}
onMenuClose={onMenuClose}
onBlur={onInputBlur}
/>
);
}
Live Demo
I am using react semantic ui dropdown and want to handle onclick event on my dropdown. consider i have this method to handle
handleClick = (e, {value }) => this.setState({id: value })
then i can call with this
<Dropdown
selection
options={myOptions}
placeholder='I change value on keyboard navigation'
onClick={this.handleClick} />
but in some circumstances i want to call handleClick with additional parameter, i've tried this
<Dropdown
selection
options={myOptions}
placeholder='I change value on keyboard navigation'
onClick={this.handleClick.bind(this,idx)} />
and change my handler to handleClick = (e, {value },idx) => this.setState({id: value }) but it seems not working. What am i doing wrong or any suggestion?
I think this part is not executing like this
onClick={this.handleClick.bind(this,idx)}
It should be change to this
onClick={(idx) => this.handleClick.bind(this,idx)}
In React when we have to pass the arguments with function on click , then we have to add arrow notation like above.
If this help doesn't work for you then I think you have to change your binding
Then Following code will help you
Change onClick={this.handleClick.bind(this,idx)} to this
onClick={(e, e.target.value, idx) => this.handleClick(e, e.target.value, idx)}
And also change
handleClick = (e, {value },idx) => this.setState({id: value })
to this
handleClick = (e, value, idx) => this.setState({id: value })