Реакция Выберите связанные параметры на основе другого выпадающего выбранного значения - PullRequest
3 голосов
/ 30 марта 2020

Использование React Выберите Asyn c, каковы лучшие практики для цепочки опций.

Что я имею в виду: у меня 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 Ответ

1 голос
/ 02 апреля 2020

Например, у меня есть поле react-select Asyn c. Я использую для управления формой 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}
/>

Это простой способ управления зависимыми тремя или более полями. Я думаю, что вы понимаете эту логику c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...