option - 根据另一个下拉选择的值 react 选择链式选项

标签 option react-select chained

使用 React Select Async 链接选项的最佳实践是什么。

我的意思是:我有 3 个下拉列表,第一个是从默认值填充的选项值,接下来的 2 个下拉列表被禁用。

选择第一个下拉值应该根据它的值填充第二个下拉选项,依此类推下一个下拉列表。

所以我一直在尝试

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;

最佳答案

例如我有 react-select异步字段。我用于管理表单 formik .首先创建字段:

<AsyncSelect
   name="first"
   ...
   onChange={(name, value) => {
     // you can write what you want but here small example what I do for other
     // two fields
     setFieldValue('second', null);
     setFieldValue('third', null);
     return setFieldValue(name, value);

   }}
/>

second field :
<AsyncSelect
   name="second"
   key={!!values.first && !!values.first.id ? values.first.id : null}
   ...
   onChange={(name, value) => {
     setFieldValue('third', null);
     return setFieldValue(name, value);
   }}
/>

有我给key并更改 key更改第一个字段的值。因为如果你不这样做second场不知道什么时候first字段更改值。如果你给 uniq 可变 key第二个可以从依赖于 first 的远程数据加载 field 。
third field :
<AsyncSelect
   name="third"
   key={!!values.third && !!values.third.id ? values.third.id : null}
   ...
   onChange={setFieldValue}
/>

这是管理依赖的三个或更多字段的简单方法。我想这你明白这个逻辑。

关于option - 根据另一个下拉选择的值 react 选择链式选项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60936093/

相关文章:

php - 在MySQL数据库,PHP中存储下拉选项

reactjs - 如何将删除 (X) 按钮添加到 React-Select 中的自定义组件 MultiValue?

javascript - 通过链式函数调用将元素添加到数组

javascript - 使用链式方法的 Eslint 缩进

jquery - 如何在php或jquery中将所有数字合并为一个

jQuery 返回选择列表中的第一个匹配项

php - 使用数组在选择元素中输出的mysql查询

file - 在 Magento 中将产品添加到购物车时,如何设置类型为 "file"的自定义选项?

javascript - Promise.then 不是函数 - 在 React 中处理多个 API 调用

react-select - 如何将 data-testid 属性添加到 react-select 组件