Wrapper above react-select
that supports pagination on menu scroll.
- Simple
- Multi
- Creatable
- Creatable with adding new options
- Initial options
- Autoload
- Debounce
- Request by page number
- Customization check of the need of load options
- Grouped options
- Custom select base
- Manual control of input value and menu opening
react-select | react-select-async-paginate |
---|---|
3.x | ^0.3.2 |
2.x | 0.3.x, 0.2.x |
1.x | 0.1.x |
npm install react-select react-select-async-paginate
or
yarn add react-select react-select-async-paginate
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
except isLoading
. And there are some new props:
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 isadditional
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.
Not required. Number. Debounce timeout for loadOptions
calls. 0
by default.
Not required. Default additional
for first request for every search.
Not required. Function. By default new options will load only after scroll menu to bottom. Arguments:
- scrollHeight
- clientHeight
- scrollTop
Should return boolean.
Not required. Function. By default new loaded options are concat with previous. Arguments:
- previous options
- loaded options
- next additional
Should return new options.
Not required. Can take any value. When this prop changed, AsyncPaginate
cleans all cached options.
Ref for take react-select
instance.
Not required. React component that will be used instead of SelectBase
from react-select
.
import AsyncPaginate from 'react-select-async-paginate';
...
/*
* assuming the API returns something like this:
* const json = {
* results: [
* {
* value: 1,
* label: 'Audi',
* },
* {
* value: 2,
* label: 'Mercedes',
* },
* {
* value: 3,
* label: 'BMW',
* },
* ],
* has_more: true,
* };
*/
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}
/>
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,
}}
/>
You can use reduceGroupedOptions
util to group options by label
key.
import AsyncPaginate, { reduceGroupedOptions } from 'react-select-async-paginate';
/*
* assuming the API returns something like this:
* const json = {
* options: [
* label: 'Cars',
* options: [
* {
* value: 1,
* label: 'Audi',
* },
* {
* value: 2,
* label: 'Mercedes',
* },
* {
* value: 3,
* label: 'BMW',
* },
* ]
* ],
* hasMore: true,
* };
*/
...
<AsyncPaginate
{...otherProps}
reduceOptions={reduceGroupedOptions}
/>
You can use AsyncPaginateBase
component.
import React, { useState } from 'react';
import { AsyncPaginateBase } 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,
};
}
const MyWrapper = ({
value,
onChange,
}) => {
const [inputValue, onInputChange] = useState('');
const [menuIsOpen, setMenuIsOpen] = useState(false);
const onMenuOpen = () => {
setMenuIsOpen(true);
};
const onMenuClose = () => {
setMenuIsOpen(false);
};
return (
<AsyncPaginate
value={value}
loadOptions={loadOptions}
onChange={onChange}
inputValue={inputValue}
onInputChange={onInputChange}
menuIsOpen={menuIsOpen}
onMenuOpen={onMenuOpen}
onMenuClose={onMenuClose}
/>
);
};
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,
}}
/>