JedWatson / JedWatson/react-select
AsyncCreatableSelect not allowing to enter more than character to search or create
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 28k
- Forks
- 4.1k
- PR merge metrics
- No merged PRs in 30d
Description
this is my code below mentioned.AsyncCreatableSelect not allowing to enter more than character to search or create please suggest
import React, { useState, useEffect, useCallback } from 'react';
import Select, { StylesConfig, ActionMeta, components } from 'react-select';
import AsyncSelect from 'react-select/async';
import makeAnimated from 'react-select/animated';
import CreatableSelect from 'react-select/creatable';
import AsyncCreatableSelect from 'react-select/async-creatable';
import ReactTooltip, { Tooltip } from "react-tooltip";
import { DropdownProps, Option } from '@/Interface/Dropdown';
import dropdownData from '@/Mockup/Dropdown.json';
import useFetch from '@/Hooks/useFetch';
import { encrypt, decrypt } from '@/Utils/CryptoUtils';
const { alignmentClassMap }: { alignmentClassMap: Record<string, string> } = dropdownData;
const getSelectContainer = (options: { tooltipText: string; tooltipId: string }) => (props: any) => (
<components.SelectContainer
{...props}
innerProps={{
...props.innerProps,
'data-tip': options.tooltipText,
'data-for': options.tooltipId,
}}
/>
);
const Dropdown: React.FC = ({
Label,
Name,
Class,
Color = '#000000',
Placeholder,
Disabled,
ReadOnly = false,
Width = 'w-full',
Alignment = 'left',
Required = false,
Options,
Value: initialValue = null,
Reset = false,
IsSearchable = false,
Rtl = false,
LabelClass,
LabelColor = "#000000",
Icon,
IsMultiSelect = false,
EnableTooltip = true,
TooltipText = "dsagfsdg",
ValidationMessage,
onChange,
OptionsUrl = null,
Async = false,
AsyncOptionsUrl = null,
Algorithm = 'AES',
Encryption = true,
Decryption = true,
BlockHelper,
CreateNew = false,
}) => {
const [selectedValue, setSelectedValue] = useState<Option | Option[] | null>(initialValue);
const [optionsToUse, setOptionsToUse] = useState<Option[]>(Options);
const { data, isLoading, error } = useFetch<Option[]>(OptionsUrl || '');
const [inputValue, setInputValue] = useState('');
const [inputValueState, setInputValueState] = useState('');
useEffect(() => {
if (OptionsUrl) {
if (data) {
setOptionsToUse(data);
}
} else {
setOptionsToUse(Options);
}
}, [Options, OptionsUrl, data]);
// load options using API call
const loadOptionsPromise = async (inputValue: string): Promise<Option[]> => {
try {
const response = await fetch(${AsyncOptionsUrl}?search=${inputValue});
const json = await response.json();
return json;
} catch (error) {
console.error('Error loading options:', error);
return [];
}
};
const handleOnInputChange = (inputValue: string) => {
let result = inputValue.substring(0,3);
const forbiddenCharacters = /[^a-zA-Z0-9.-_ ]/g;
if(result.match(forbiddenCharacters)){
result = result.replace(forbiddenCharacters, '');
}
setInputValueState(result);
}
const createOption = (label: string) => ({
label,
value: label.toLowerCase().replace(/\W/g, ''),
});
const handleCreate = (inputValue: string) => {
const newOption = createOption(inputValue);
setOptionsToUse((prev) => {
const updatedOptions = [...prev, newOption];
console.log('Updated optionsToUse:', updatedOptions);
return updatedOptions;
});
console.info('optionstoUse',optionsToUse)
if (IsMultiSelect && Array.isArray(selectedValue)) {
const newSelectedValues = [...selectedValue, newOption];
setSelectedValue(newSelectedValues);
} else {
setSelectedValue(newOption);
}
setInputValue('');
};
const customStyles: StylesConfig<Option, boolean> = {
control: (provided, state) => ({
...provided,
width: '100%',
color: Color,
direction: Rtl ? 'rtl' : 'ltr',
borderColor: state.isFocused
? (Required && (!selectedValue || (Array.isArray(selectedValue) && selectedValue.length === 0)))
? 'red' : provided.borderColor
: provided.borderColor,
'&:hover': {
borderColor: state.isFocused
? (Required && (!selectedValue || (Array.isArray(selectedValue) && selectedValue.length === 0)))
? 'red' : provided.borderColor
: provided.borderColor,
},
}),
placeholder: (provided) => ({
...provided,
color: Color,
}),
singleValue: (provided) => ({
...provided,
color: Color,
}),
multiValue: (provided) => ({
...provided,
}),
multiValueLabel: (provided) => ({
...provided,
color: Color,
}),
multiValueRemove: (provided) => ({
...provided,
color: Color,
}),
input: (provided) => ({
...provided,
color: Color,
}),
};
const animatedComponents = makeAnimated();
// Handle change in selected value(s)
const handleChange = (option: any) => {
setSelectedValue(option);
if (onChange) {
onChange(Name, option);
}
};
const handleInputChange = (newValue: string) => {
setInputValue(newValue);
return newValue;
};
useEffect(() => {
if (Encryption && selectedValue) {
const encryptedValue = Array.isArray(selectedValue)
? selectedValue.map(val => encrypt(val.value, Algorithm)).join(',')
: encrypt(selectedValue.value, Algorithm);
const selectElement = document.querySelector([name="${Name}"]);
if (selectElement) {
selectElement.setAttribute('data-encrypted-value', encryptedValue);
}
}
}, [selectedValue, Encryption, Algorithm, Name]);
if (isLoading) return
Loading...
;if (error) return Error: {error.message};
return (
<div className={relative }>
{/* Label */}
{Label && (
<label className={${LabelClass}} style={{ color: LabelColor }}>
{Label}
)}
{/* Dropdown */}
<div className={`relative flex items-center ${!Rtl ? alignmentClassMap[Alignment] : ''}`}>
{Async ?
CreateNew ? // Async - true, create New Option - true
<AsyncCreatableSelect
name={Name}
classNamePrefix="react-select"
placeholder={Placeholder}
isDisabled={Disabled || ReadOnly}
isMulti={IsMultiSelect}
isClearable={Reset}
loadOptions={loadOptionsPromise}
defaultOptions={optionsToUse}
options={optionsToUse}
defaultValue={initialValue}
value={selectedValue}
onChange={(option: any) => handleChange(option)}
allowCreateWhileLoading={true}
isSearchable={IsSearchable}
onInputChange={handleInputChange}
inputValue={inputValue}
noOptionsMessage={() => (isLoading ? 'Loading...' : 'No options')}
styles={customStyles}
components={{
SelectContainer: getSelectContainer({
tooltipText: TooltipText,
tooltipId: 'currency-tooltip',
}),
}}
data-tooltip-id={EnableTooltip ? "currency-tooltip" : undefined}
data-tooltip-content={EnableTooltip ? TooltipText : ValidationMessage}
className={`${Class} ${Width} border ${Required && (!selectedValue || (Array.isArray(selectedValue) && selectedValue.length === 0)) ? 'border-red-500' : 'border-gray-300'}`}
onCreateOption={handleCreate}
/>
:
<AsyncSelect // Async - true, create New Option - false
name={Name}
classNamePrefix="react-select"
placeholder={Placeholder}
isDisabled={Disabled || ReadOnly}
isMulti={IsMultiSelect}
isClearable={Reset}
loadOptions={loadOptionsPromise}
defaultOptions={optionsToUse}
options={optionsToUse}
defaultValue={initialValue}
value={selectedValue}
onChange={(option) => handleChange(option)}
isSearchable={IsSearchable}
onInputChange={handleInputChange}
inputValue={inputValue}
noOptionsMessage={() => (isLoading ? 'Loading...' : 'No options')}
styles={customStyles}
components={{
SelectContainer: getSelectContainer({
tooltipText: TooltipText,
tooltipId: 'currency-tooltip',
}),
}}
data-tooltip-id={EnableTooltip ? "currency-tooltip" : undefined}
data-tooltip-content={EnableTooltip ? TooltipText : ValidationMessage}
className={`${Class} ${Width} border ${Required && (!selectedValue || (Array.isArray(selectedValue) && selectedValue.length === 0)) ? 'border-red-500' : 'border-gray-300'}`}
/>
: //Normal Select Async False
CreateNew ? //Async false create New
<CreatableSelect
name={Name}
classNamePrefix="react-select"
placeholder={Placeholder}
isDisabled={Disabled || ReadOnly}
isMulti={IsMultiSelect}
isClearable={Reset}
options={optionsToUse}
defaultValue={initialValue}
value={selectedValue}
onChange={(option) => handleChange(option)}
isSearchable={IsSearchable}
styles={customStyles}
components={{
SelectContainer: getSelectContainer({
tooltipText: TooltipText,
tooltipId: 'currency-tooltip',
}),
}}
data-tooltip-id={EnableTooltip ? "currency-tooltip" : undefined}
data-tooltip-content={EnableTooltip ? TooltipText : ValidationMessage}
className={`${Class} ${Width} border ${Required && (!selectedValue || (Array.isArray(selectedValue) && selectedValue.length === 0)) ? 'border-red-500' : 'border-gray-300'}`}
onCreateOption={handleCreate}
/>
: //Async false create New false
<Select
name={Name}
classNamePrefix="react-select"
placeholder={Placeholder}
isDisabled={Disabled || ReadOnly}
isMulti={IsMultiSelect}
isClearable={Reset}
options={optionsToUse}
defaultValue={initialValue}
value={selectedValue}
onChange={(option) => handleChange(option)}
isSearchable={IsSearchable}
styles={customStyles}
components={{
SelectContainer: getSelectContainer({
tooltipText: TooltipText,
tooltipId: 'currency-tooltip',
}),
}}
data-tooltip-id={EnableTooltip ? "currency-tooltip" : undefined}
data-tooltip-content={EnableTooltip ? TooltipText : ValidationMessage}
className={`${Class} ${Width} border ${Required && (!selectedValue || (Array.isArray(selectedValue) && selectedValue.length === 0)) ? 'border-red-500' : 'border-gray-300'}`}
/>
}
</div>
{/* Tooltip */}
{EnableTooltip && (
<Tooltip variant="info" place="bottom" id='currency-tooltip' />
)}
{/* Block Helper */}
{BlockHelper && (
<p className="mt-2 text-sm text-gray-500"> {(BlockHelper || '')} </p>
)}
{/* Validation Message */}
{Required && (!selectedValue || (Array.isArray(selectedValue) && selectedValue.length === 0)) && ValidationMessage && (
<span className="validation-message">
{ValidationMessage}
</span>
)}
</div>
);
};
export default Dropdown;
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the reported behavior with the shown AsyncCreatableSelect configuration, especially the handleInputChange path and its inputValue state. Compare the entered value with the three-character substring used there; done when the intended search or creation input is accepted beyond one character and the behavior is demonstrated consistently.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100