Implementing react-select Async with React Hooks and applying Pagination
Tags: react, javascript
Published: 2020-12-27 22:21:55

This tutorial is about implementing the React Component react-select with Async AJAX request and pagination applied. As we won't implement the BackEnd code, we can just use the OpenSource API: https://www.anapioficeandfire.com/api


Very often we need to create a Select Dropdown for our React application. It will be populated dynamically with information from the database and the AJAX endpoint can be changed based on another React State. 

For example: You need to load the **street names** of a **city**. So in this case, when the **city** property change, a new AJAX request needs to be made to populate the Select Dropdown with the new options. But what happens if the backend returns hundreds (or thousands) of **street names**? It will overload the BackEnd and FrontEnd. To avoid overloading the best practice is to apply request pagination to show certain number of options as the user needs.  

####Installing required components:
**React-Select** is extremely easy to use dropdown library exclusively built for React. The react-select library offers powerful multi-select, autocomplete, and AJAX support without any hassle. React-select’s main power lies in its dynamic functionalities such as search, filter, async loading, animated component, easy accessibility, and faster loading time. `npm install react-select`

**react-select-async-paginate** - wrapper above react-select that supports pagination on menu scroll. `npm install react-select-async-paginate`

First clean the `App.js` up and implement the Static Options Select that will update the State and reload the Async Select:

```javascript
import React, { useState } from 'react';
import Select from "react-select";

const App = () => {
   const options = [
       { value: "The Crownlands" },
       { value: "Iron Islands" },
       { value: "The North" },
       { value: "The Reach" },
       { value: "The Riverlands" },
       { value: "The Vale" },
       { value: "The Westerlands" },
       { value: "The Stormlands" }
   ];
  const [region, setRegion] = useState(options[0]);
  const [currentCountry, setCurrentCountry] = useState(null);
  const onchangeSelect = (item) => {
    setCurrentCountry(null);
    setRegion(item);
  };
  return (
    <div className="App">
       <Select
          value={region}
          onChange={onchangeSelect}
          options={options}
          getOptionValue={(option) => option.value}
          getOptionLabel={(option) => option.value}
       />
    </div>
  );
};

export default App;
```

Second, let's create a new file `SelectAsyncPaginate.js` to implement the Async Select. When a new option is selected it will call a `Props` function to update the external state in `App.js`:

```javascript
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { AsyncPaginate } from "react-select-async-paginate";

const SelectAsyncPaginate = (props) => {
  const [regionName, setRegionName] = useState(null);

  useEffect(() => {
    setRegionName(props.regionName);
  }, [props.regionName]);

  const loadOptions = async (searchQuery, loadedOptions, { page }) => {
    
    const response = await fetch(
      `https://www.anapioficeandfire.com/api/houses?region=${regionName}&page=${page}&pageSize=10`
    );
    const responseJSON = await response.json();

    return {
      options: responseJSON,
      hasMore: responseJSON.length >= 1,
      additional: {
        page: searchQuery ? 2 : page + 1,
      },
    };
  };

  const onChange = (option) => {
    if (typeof props.onChange === "function") {
      props.onChange(option);
    }
  };

  return (
    <AsyncPaginate
      key={JSON.stringify(regionName)}
      value={props.value || ""}
      loadOptions={loadOptions}
      getOptionValue={(option) => option.name}
      getOptionLabel={(option) => option.name}
      onChange={onChange}
      isSearchable={false}
      placeholder="Select House"
      additional={{
        page: 1,
      }}
    />
  );
};

SelectAsyncPaginate.propTypes = {
  regionName: PropTypes.string.isRequired,
  value: PropTypes.object,
  onChange: PropTypes.func,
};

export default SelectAsyncPaginate;

```

Now we need to include the new `SelectAsyncPaginate` function to the `App.js`:
```javascript
 ...
 function App() {
 ...
    <div className="App">
        <Select
          value={region}
          onChange={onchangeSelect}
          options={options}
          getOptionValue={(option) => option.value}
          getOptionLabel={(option) => option.value}
       /> 
       <SelectAsyncPaginate
         regionName={region.value}
         value={currentCountry}
         onChange={(country) => setCurrentCountry(country)}
       />
    </div>
 ...
 }

```


The result:
![react-select-async-paginate](https://dev-to-uploads.s3.amazonaws.com/i/5p8unjly5czls8f1swy8.gif)

Code available on my [GitHub](https://github.com/helmarjunior/react-hooks-select-async).

#References
* https://github.com/JedWatson/react-select
* https://github.com/vtaits/react-select-async-paginate/tree/master/packages/react-select-async-paginate