react-select-module
Version:
A Select control built with and for ReactJS
157 lines (153 loc) • 4.57 kB
JavaScript
import React, { Component } from 'react';
import { handleInputChange } from './utils';
import manageState from './stateManager';
import Select from './Select';
export const defaultProps = {
cacheOptions: false,
defaultOptions: false,
filterOption: null,
isLoading: false,
};
export const makeAsyncSelect = SelectComponent =>
class Async extends Component {
static defaultProps = defaultProps;
select;
lastRequest;
mounted = false;
optionsCache = {};
constructor(props) {
super();
this.state = {
defaultOptions: Array.isArray(props.defaultOptions)
? props.defaultOptions
: undefined,
inputValue:
typeof props.inputValue !== 'undefined' ? props.inputValue : '',
isLoading: props.defaultOptions === true,
loadedOptions: [],
passEmptyOptions: false,
};
}
componentDidMount() {
this.mounted = true;
const { defaultOptions } = this.props;
const { inputValue } = this.state;
if (defaultOptions === true) {
this.loadOptions(inputValue, options => {
if (!this.mounted) return;
const isLoading = !!this.lastRequest;
this.setState({ defaultOptions: options || [], isLoading });
});
}
}
UNSAFE_componentWillReceiveProps(nextProps) {
// if the cacheOptions prop changes, clear the cache
if (nextProps.cacheOptions !== this.props.cacheOptions) {
this.optionsCache = {};
}
if (nextProps.defaultOptions !== this.props.defaultOptions) {
this.setState({
defaultOptions: Array.isArray(nextProps.defaultOptions)
? nextProps.defaultOptions
: undefined,
});
}
}
componentWillUnmount() {
this.mounted = false;
}
focus() {
this.select.focus();
}
blur() {
this.select.blur();
}
loadOptions(inputValue, callback) {
const { loadOptions } = this.props;
if (!loadOptions) return callback();
const loader = loadOptions(inputValue, callback);
if (loader && typeof loader.then === 'function') {
loader.then(callback, () => callback());
}
}
handleInputChange = (newValue, actionMeta) => {
const { cacheOptions, onInputChange } = this.props;
// TODO
const inputValue = handleInputChange(newValue, actionMeta, onInputChange);
if (!inputValue) {
delete this.lastRequest;
this.setState({
inputValue: '',
loadedInputValue: '',
loadedOptions: [],
isLoading: false,
passEmptyOptions: false,
});
return;
}
if (cacheOptions && this.optionsCache[inputValue]) {
this.setState({
inputValue,
loadedInputValue: inputValue,
loadedOptions: this.optionsCache[inputValue],
isLoading: false,
passEmptyOptions: false,
});
} else {
const request = (this.lastRequest = {});
this.setState(
{
inputValue,
isLoading: true,
passEmptyOptions: !this.state.loadedInputValue,
},
() => {
this.loadOptions(inputValue, options => {
if (!this.mounted) return;
if (options) {
this.optionsCache[inputValue] = options;
}
if (request !== this.lastRequest) return;
delete this.lastRequest;
this.setState({
isLoading: false,
loadedInputValue: inputValue,
loadedOptions: options || [],
passEmptyOptions: false,
});
});
}
);
}
return inputValue;
};
render() {
const { loadOptions, isLoading: isLoadingProp, ...props } = this.props;
const {
defaultOptions,
inputValue,
isLoading,
loadedInputValue,
loadedOptions,
passEmptyOptions,
} = this.state;
const options = passEmptyOptions
? []
: inputValue && loadedInputValue
? loadedOptions
: defaultOptions || [];
return (
<SelectComponent
{...props}
ref={ref => {
this.select = ref;
}}
options={options}
isLoading={isLoading || isLoadingProp}
onInputChange={this.handleInputChange}
/>
);
}
};
const SelectState = manageState(Select);
export default makeAsyncSelect(SelectState);