A Select control built with and for ReactJS that provides comprehensive dropdown functionality with support for single/multi-select, async data loading, search filtering, and extensive customization.
—
React-Select provides powerful hooks for advanced component integration and custom state management. These hooks allow you to build custom select implementations while leveraging React-Select's core functionality.
Core state management hook that provides the same functionality as the main Select component but with full control over rendering.
/**
* State management hook providing full control over select state and behavior
* @param props - State manager props with default values and event handlers
* @returns Complete state object and methods for managing select behavior
*/
function useStateManager<
Option = unknown,
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>
>(props: StateManagerProps<Option, IsMulti, Group>): StateManagerReturn<Option, IsMulti, Group>;
interface StateManagerProps<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
/** Default input value */
defaultInputValue?: string;
/** Default menu open state */
defaultMenuIsOpen?: boolean;
/** Default selected value(s) */
defaultValue?: PropsValue<Option>;
/** Input value for controlled input */
inputValue?: string;
/** Menu open state for controlled menu */
menuIsOpen?: boolean;
/** Selected value(s) for controlled component */
value?: PropsValue<Option>;
/** Change handler for value updates */
onChange?: (newValue: OnChangeValue<Option, IsMulti>, actionMeta: ActionMeta<Option>) => void;
/** Input change handler */
onInputChange?: (newValue: string, actionMeta: InputActionMeta) => void;
/** Menu open handler */
onMenuOpen?: () => void;
/** Menu close handler */
onMenuClose?: () => void;
}
interface StateManagerReturn<Option, IsMulti extends boolean, Group extends GroupBase<Option>> {
/** Current input value */
inputValue: string;
/** Current menu open state */
menuIsOpen: boolean;
/** Current selected value(s) */
value: PropsValue<Option>;
/** Set input value */
setValue: (newValue: PropsValue<Option>, actionMeta: ActionMeta<Option>) => void;
/** Set input text */
setInputValue: (newValue: string, actionMeta: InputActionMeta) => void;
/** Set menu open state */
setMenuIsOpen: (newValue: boolean, actionMeta: ActionMeta<Option>) => void;
}Usage Examples:
import { useStateManager } from "react-select";
// Custom select with external state control
const CustomSelect = () => {
const {
inputValue,
menuIsOpen,
value,
setValue,
setInputValue,
setMenuIsOpen
} = useStateManager({
defaultValue: null,
defaultInputValue: "",
defaultMenuIsOpen: false,
onChange: (newValue, actionMeta) => {
console.log("Value changed:", newValue, actionMeta);
setValue(newValue, actionMeta);
}
});
// Custom rendering logic using state
return (
<div>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value, { action: 'input-change' })}
/>
<button onClick={() => setMenuIsOpen(!menuIsOpen)}>
{menuIsOpen ? 'Close' : 'Open'} Menu
</button>
{/* Custom menu rendering based on state */}
</div>
);
};Hook for implementing async option loading functionality in custom select components.
/**
* Async data loading hook for dynamic option fetching
* @param props - Async configuration with load function and caching options
* @returns Async state and methods for loading options
*/
function useAsync<Option = unknown>(props: AsyncProps<Option>): AsyncState<Option>;
interface AsyncProps<Option> {
/** Default options to show before any input */
defaultOptions?: OptionsOrGroups<Option, Group> | boolean;
/** Enable caching of loaded options */
cacheOptions?: any;
/** Function to load options based on input value */
loadOptions?: (
inputValue: string,
callback: (options: OptionsOrGroups<Option, Group>) => void
) => Promise<OptionsOrGroups<Option, Group>> | void;
}
interface AsyncState<Option> {
/** Currently loaded options */
options: OptionsOrGroups<Option, Group>;
/** Loading state indicator */
isLoading: boolean;
/** Error state from failed loads */
error: Error | null;
/** Function to trigger option loading */
loadOptions: (inputValue: string) => void;
/** Function to clear loaded options */
clearOptions: () => void;
}Usage Examples:
import { useAsync } from "react-select";
// Custom async select implementation
const CustomAsyncSelect = () => {
const {
options,
isLoading,
error,
loadOptions
} = useAsync({
defaultOptions: true,
cacheOptions: true,
loadOptions: async (inputValue: string) => {
const response = await fetch(`/api/search?q=${inputValue}`);
const data = await response.json();
return data.map((item: any) => ({
value: item.id,
label: item.name
}));
}
});
return (
<div>
<input
onChange={(e) => loadOptions(e.target.value)}
placeholder="Type to search..."
/>
{isLoading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
<ul>
{options.map((option) => (
<li key={option.value}>{option.label}</li>
))}
</ul>
</div>
);
};Hook for implementing creatable option functionality in custom select components.
/**
* Creatable options hook for dynamic option creation
* @param props - Creatable configuration with creation handlers
* @returns Creatable state and methods for managing option creation
*/
function useCreatable<Option = unknown>(props: CreatableProps<Option>): CreatableState<Option>;
interface CreatableProps<Option> {
/** Allow creating new options */
allowCreateWhileLoading?: boolean;
/** Handler for creating new options */
onCreateOption?: (inputValue: string) => void;
/** Function to determine if new option can be created */
isValidNewOption?: (inputValue: string, selectValue: Options<Option>, selectOptions: OptionsOrGroups<Option, Group>) => boolean;
/** Function to get new option data */
getNewOptionData?: (inputValue: string, optionLabel: ReactNode) => Option;
/** Function to format create option label */
formatCreateLabel?: (inputValue: string) => ReactNode;
/** Function to determine if option was created by user */
isOptionDisabled?: (option: Option, selectValue: Options<Option>) => boolean;
}
interface CreatableState<Option> {
/** Current options including created ones */
options: OptionsOrGroups<Option, Group>;
/** Whether a new option can be created for current input */
canCreateOption: boolean;
/** Function to create new option */
createOption: (inputValue: string) => void;
/** Function to check if option is newly created */
isNewOption: (option: Option) => boolean;
}Usage Examples:
import { useCreatable } from "react-select";
// Custom creatable select implementation
const CustomCreatableSelect = () => {
const [options, setOptions] = useState([
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" }
]);
const {
canCreateOption,
createOption,
isNewOption
} = useCreatable({
onCreateOption: (inputValue: string) => {
const newOption = {
value: inputValue.toLowerCase(),
label: inputValue,
__isNew__: true
};
setOptions([...options, newOption]);
},
isValidNewOption: (inputValue, selectValue, selectOptions) => {
return inputValue.length > 0 &&
!selectOptions.some(option => option.label.toLowerCase() === inputValue.toLowerCase());
},
formatCreateLabel: (inputValue) => `Create "${inputValue}"`
});
return (
<div>
{/* Custom implementation using creatable state */}
</div>
);
};React-Select supports both controlled and uncontrolled patterns through the state manager hooks.
Controlled Pattern:
// Fully controlled select with external state
const ControlledSelect = () => {
const [value, setValue] = useState(null);
const [inputValue, setInputValue] = useState("");
const [menuIsOpen, setMenuIsOpen] = useState(false);
const stateManager = useStateManager({
value,
inputValue,
menuIsOpen,
onChange: setValue,
onInputChange: setInputValue,
onMenuOpen: () => setMenuIsOpen(true),
onMenuClose: () => setMenuIsOpen(false)
});
// Use stateManager for rendering
};Uncontrolled Pattern:
// Uncontrolled select with default values
const UncontrolledSelect = () => {
const stateManager = useStateManager({
defaultValue: { value: "default", label: "Default Option" },
defaultInputValue: "",
defaultMenuIsOpen: false,
onChange: (value, actionMeta) => {
// Handle changes without controlling state
console.log("Selection changed:", value);
}
});
// Use stateManager for rendering
};React-Select hooks integrate seamlessly with Redux, Zustand, or other state management libraries.
// Redux integration example
const ReduxSelect = () => {
const dispatch = useDispatch();
const selectState = useSelector(state => state.select);
const stateManager = useStateManager({
value: selectState.value,
inputValue: selectState.inputValue,
menuIsOpen: selectState.menuIsOpen,
onChange: (value, actionMeta) => {
dispatch(updateSelectValue({ value, actionMeta }));
},
onInputChange: (inputValue, actionMeta) => {
dispatch(updateInputValue({ inputValue, actionMeta }));
}
});
// Render using state manager
};Complete props interface for useStateManager hook.
interface StateManagerProps<Option, IsMulti extends boolean, Group extends GroupBase<Option>>
extends Omit<PublicBaseSelectProps<Option, IsMulti, Group>, StateManagedPropKeys> {
defaultInputValue?: string;
defaultMenuIsOpen?: boolean;
defaultValue?: PropsValue<Option>;
}
type StateManagedPropKeys =
| 'inputValue'
| 'menuIsOpen'
| 'onChange'
| 'onInputChange'
| 'onMenuClose'
| 'onMenuOpen'
| 'value';State interfaces returned by async and creatable hooks.
interface AsyncState<Option> {
options: OptionsOrGroups<Option, Group>;
isLoading: boolean;
error: Error | null;
loadOptions: (inputValue: string) => void;
clearOptions: () => void;
}
interface CreatableState<Option> {
options: OptionsOrGroups<Option, Group>;
canCreateOption: boolean;
createOption: (inputValue: string) => void;
isNewOption: (option: Option) => boolean;
}Install with Tessl CLI
npx tessl i tessl/npm-react-select