
react-select-async-paginate
Wrapper above react-select that supports pagination on menu scroll.
Sandbox examples
Versions
| 5.x | 0.6.x, 0.7.x |
| 4.x | 0.5.x |
| 3.x | 0.5.x, 0.4.x, ^0.3.2 |
| 2.x | 0.3.x, 0.2.x |
| 1.x | 0.1.x |
Installation
npm install react-select react-select-async-paginate
or
yarn add react-select react-select-async-paginate
or
bun add react-select react-select-async-paginate
Usage
AsyncPaginate is an alternative of Async but supports loading page by page. It is wrapper above default react-select thus it accepts all props of default Select. And there are some new props:
loadOptions
Required. Async function that take next arguments:
- Current value of search input.
- Loaded options for current search.
- Collected additional data e.g. current page number etc. For first load it is
additional from props, for next is additional from previous response for current search. null by default.
It should return next object:
{
options: Array,
hasMore: boolean,
additional?: any,
}
It similar to loadOptions from Select.Async but there is some differences:
- Loaded options as 2nd argument.
- Additional data as 3nd argument.
- Not supports callback.
- Should return
hasMore for detect end of options list for current search.
debounceTimeout
Not required. Number. Debounce timeout for loadOptions calls. 0 by default.
additional
Not required. Default additional for first request for every search.
defaultAdditional
Not required. Default additional for empty search if options or defaultOptions defined.
shouldLoadMore
Not required. Function. By default new options will load only after scroll menu to bottom. Arguments:
- scrollHeight
- clientHeight
- scrollTop
Should return boolean.
reduceOptions
Not required. Function. By default new loaded options are concat with previous. Arguments:
- previous options
- loaded options
- next additional
Should return new options.
reloadOnErrorTimeout
Not required. Number. Time in milliseconds to retry a request after an error
clearCacheOnSearchChange
Not required. Boolean. Clear all cached options on search change
Not required. Boolean. Clear all cached options on menu close
cacheUniqs
Not required. Array. Works as 2nd argument of useEffect hook. When one of items changed, AsyncPaginate cleans all cached options.
Not required. Boolean. If false options will not load on menu opening.
Not required. Function. Post-mapping of loaded options to display them in the menu. Can be used to put selected options to top of the list.
selectRef
Ref for take react-select instance.
Example
offset way
import { AsyncPaginate } from 'react-select-async-paginate';
...
async function loadOptions(search, loadedOptions) {
const response = await fetch(`/awesome-api-url/?search=${search}&offset=${loadedOptions.length}`);
const responseJSON = await response.json();
return {
options: responseJSON.results,
hasMore: responseJSON.has_more,
};
}
<AsyncPaginate
value={value}
loadOptions={loadOptions}
onChange={setValue}
/>
page way
import { AsyncPaginate } from 'react-select-async-paginate';
...
async function loadOptions(search, loadedOptions, { page }) {
const response = await fetch(`/awesome-api-url/?search=${search}&page=${page}`);
const responseJSON = await response.json();
return {
options: responseJSON.results,
hasMore: responseJSON.has_more,
additional: {
page: page + 1,
},
};
}
<AsyncPaginate
value={value}
loadOptions={loadOptions}
onChange={setValue}
additional={{
page: 1,
}}
/>
Grouped options
You can use reduceGroupedOptions util to group options by label key.
import { AsyncPaginate, reduceGroupedOptions } from 'react-select-async-paginate';
...
<AsyncPaginate
{...otherProps}
reduceOptions={reduceGroupedOptions}
/>
Replacing react-select component
You can use withAsyncPaginate HOC.
import { withAsyncPaginate } from 'react-select-async-paginate';
...
const CustomAsyncPaginate = withAsyncPaginate(CustomSelect);
typescript
Describing type of component with extra props (example with Creatable):
import type { ReactElement } from 'react';
import type { GroupBase } from 'react-select';
import Creatable from 'react-select/creatable';
import type { CreatableProps } from 'react-select/creatable';
import { withAsyncPaginate } from 'react-select-async-paginate';
import type {
UseAsyncPaginateParams,
ComponentProps,
} from 'react-select-async-paginate';
type AsyncPaginateCreatableProps<
OptionType,
Group extends GroupBase<OptionType>,
Additional,
IsMulti extends boolean,
> =
& CreatableProps<OptionType, IsMulti, Group>
& UseAsyncPaginateParams<OptionType, Group, Additional>
& ComponentProps<OptionType, Group, IsMulti>;
type AsyncPaginateCreatableType = <
OptionType,
Group extends GroupBase<OptionType>,
Additional,
IsMulti extends boolean = false,
>(props: AsyncPaginateCreatableProps<OptionType, Group, Additional, IsMulti>) => ReactElement;
const AsyncPaginateCreatable = withAsyncPaginate(Creatable) as AsyncPaginateCreatableType;
Replacing Components
Usage of replacing components is similar with react-select, but there is one difference. If you redefine MenuList you should wrap it with wrapMenuList for workaround of some internal bugs of react-select.
import { AsyncPaginate, wrapMenuList } from 'react-select-async-paginate';
...
const MenuList = wrapMenuList(CustomMenuList);
<AsyncPaginate
{...otherProps}
components={{
...otherComponents,
MenuList,
}}
/>
Extended usage
If you want construct own component that uses logic of react-select-async-paginate inside, you can use next hooks:
useAsyncPaginate
useAsyncPaginateBase
useComponents
import {
useAsyncPaginate,
useComponents,
} from 'react-select-async-paginate';
...
const CustomAsyncPaginateComponent = ({
options,
defaultOptions,
additional,
loadOptionsOnMenuOpen,
debounceTimeout,
filterOption,
reduceOptions,
shouldLoadMore,
components: defaultComponents,
value,
onChange,
}) => {
const asyncPaginateProps = useAsyncPaginate({
options,
defaultOptions,
additional,
loadOptionsOnMenuOpen,
debounceTimeout,
filterOption,
reduceOptions,
shouldLoadMore,
});
const components = useComponents(defaultComponents);
return (
<CustomSelect
{...asyncPaginateProps}
components={components}
value={value}
onChange={onChange}
/>
);
}
useComponents provides redefined MenuList component by default. If you want redefine it, you should also wrap in with wrapMenuList.