Using React Select Async what are the best practices to chain options.
What I mean: I have 3 dropdowns the first one is populated from default with option values and the next 2 dropdowns are disabled.
Selecting the first dropdown value should populate the second dropdown options based on it's value and so on with the next dropdown.
so what I've been trying
import React from "react";
import Select from "react-select";
import AsyncSelect from "react-select/async";
import classnames from "classnames";
import Requests from "../services/requests";
const filterOptions = (inputValue, options) => {
return options.filter(i =>
i.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
class FieldsRenderer extends React.Component {
constructor(props) {
super(props);
this.state = {
fields: props.fields,
containerClass: props.containerClass,
stepSnapshot: null,
selectOptions: {}
};
this.props.fields.map( (f) => {
if(f.type === 'select' && typeof f.dependsOn !== 'undefined') {
this.state.selectOptions[f.name] = null;
}
})
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.fields !== prevState.fields) {
return {
fields: nextProps.fields,
containerClass: nextProps.containerClass
};
}
return null;
}
componentDidUpdate(prevProps, nextProps) {
if (prevProps !== this.props) {
this.setState({
fields: nextProps.fields,
containerClass: nextProps.containerClass
});
this.props.fields.map(f => {
if (typeof f.dependsOn != "undefined") {
this.state.selectOptions[f.name] = null;
}
});
}
}
handleInputChange = (index, e) => {
console.log(e.target.value);
console.log(index);
};
handleSelectChange = (selectedOption, item) => {
this.setState({
stepSnapshot: {
[item.name]: {
value: selectedOption.value,
label: selectedOption.label
}
}
});
let childField = this.props.fields.filter(t => {
if (t.type === "select" && typeof t.dependsOn !== "undefined") {
return t.dependsOn === item.name;
}
});
if (childField) {
this.loadChildOptions(childField[0], selectedOption);
}
};
//load child slect options
loadChildOptions(target, parentValue) {
Requests.get(
process.env.REACT_APP_API_BASE_URL +
target.source +
"/" +
parentValue.value,
(status, data) => {
//data will be set but will be shown just the previous state
this.state.selectOptions[target.name] = data;
}
);
}
render() {
let containerClass = "";
let fields = this.state.fields.map((field, i) => {
const fieldType = field.type;
let fieldStyle;
if (
typeof this.state.containerClass !== "undefined" &&
this.state.containerClass !== ""
) {
containerClass = this.state.containerClass;
}
if (typeof field.width !== "undefined" && field.width !== "") {
fieldStyle = {
width: "calc(" + field.width + " - 5px)"
};
}
switch (fieldType) {
case "select": {
const selectCustomStyles = {
control: (base, state) => ({
...base,
boxShadow: state.isFocused ? 0 : 0,
borderWidth: 2,
height: 45,
borderColor: state.isFocused ? "#707070" : base.borderColor,
"&:hover": {
borderColor: state.isFocused ? "#707070" : base.borderColor
}
}),
option: (provided, state) => ({
...provided,
backgroundColor: state.isSelected ? "#46B428" : "initial"
})
};
if (
typeof field.async !== "undefined" &&
typeof field.dependsOn === "undefined"
) {
return (
<div key={i} className={"field-wrapper"}>
<AsyncSelect
loadOptions={(inputValue, callback) => {
Requests.get(
process.env.REACT_APP_API_BASE_URL + field.source,
(status, data) => {
callback(data);
}
);
}}
styles={selectCustomStyles}
defaultOptions
name={field.name}
placeholder={field.label}
onChange={this.handleSelectChange}
/>
</div>
);
} else if(typeof field.dependsOn !== "undefined") {
return(<div key={i} className={"field-wrapper"}>
<AsyncSelect
styles={selectCustomStyles}
placeholder={field.label}
defaultOptions={this.state.selectOptions[field.name]}
loadOptions={this.state.selectOptions[field.name]}
/>
</div>)
} else {
const disabled =
typeof field.dependsOn !== "undefined" && field.dependsOn !== ""
? this.state.selectOptions[field.name] != null
? false
: true
: false;
return (
<div key={i} className={"field-wrapper"}>
<Select
styles={selectCustomStyles}
placeholder={field.label}
//isLoading={this.state.selectOptions[field.name].length ? true : false}
isDisabled={disabled}
name={field.name}
options={this.state.selectOptions[field.name]}
/>
</div>
);
}
}
case "input":
{
let suffix;
let inputAppendClass;
if (typeof field.suffix !== "undefined" && field.suffix !== "") {
inputAppendClass = "input-has-append";
suffix = <span className={"input-append"}>{field.suffix}</span>;
}
return (
<div
key={i}
className={classnames("field-wrapper input", inputAppendClass)}
style={fieldStyle}
>
<input
placeholder={field.label}
type="text"
className={"input-field"}
onChange={event => this.handleInputChange(field.name, event)}
/>
{suffix}
</div>
);
}
break;
case "checkbox":
{
containerClass = "checkbox-fields";
let radios = field.options.map((option, b) => {
return (
<div key={i + b} className={"field-wrapper checkbox-button"}>
<input
placeholder={option.label}
id={option.name + "_" + i + b}
type={"checkbox"}
className={"input-field"}
/>
<label htmlFor={option.name + "_" + i + b}>
<div className={"label-name"}>{option.label}</div>
<span className={"info-icon"}></span>
<div className={"hint"}>{option.hint}</div>
</label>
</div>
);
});
return radios;
}
break;
case "radio":
{
let radios = field.options.map((option, k) => {
return (
<div key={i + k} className={"field-wrapper radio-button"}>
<input
name={option.name}
id={option.name + "_" + i + k}
placeholder={option.label}
type={"radio"}
className={"input-field"}
/>
<label htmlFor={option.name + "_" + i + k}>
<div className={"label-name"}>{option.label}</div>
<div className={"hint"}>{option.hint}</div>
</label>
</div>
);
});
return radios;
}
break;
default:
break;
}
});
return (
<div className={classnames("fields-group", containerClass)}>{fields}</div>
);
}
}
export default FieldsRenderer;