React 根据另一个下拉选择值选择链式选项

Posted

技术标签:

【中文标题】React 根据另一个下拉选择值选择链式选项【英文标题】:React Select chained options based on another dropdown selected value 【发布时间】:2020-07-11 03:34:42 【问题描述】:

使用 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;

【问题讨论】:

【参考方案1】:

例如我有react-select Async 字段。我用于管理表单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字段:

<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 字段。 和third字段:

<AsyncSelect
   name="third"
   key=!!values.third && !!values.third.id ? values.third.id : null
   ...
   onChange=setFieldValue
/>

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

【讨论】:

以上是关于React 根据另一个下拉选择值选择链式选项的主要内容,如果未能解决你的问题,请参考以下文章

使用 AJAX、PHP 和 MySQL 链式填充 HTML 选择框

django:在管理员中智能选择 ChainedForeignKey / 链式下拉菜单

React - 需要下拉菜单来选择正确的选项(下拉菜单的选项值表示为字符串数字)

如何根据另一个下拉菜单值禁用下拉菜单

根据使用 Jexcel 选择的另一个下拉列表更改下拉列表值

js如何根据下拉框的每个选项,限制另一个复选框小组的选择个数