React-选中复选框时渲染类组件

时间:2019-01-25 19:14:50

标签: javascript reactjs

我有一个选择下拉菜单,当选择该下拉菜单时,它会使用formik中的<FieldArray>来呈现一个复选框组

  <FieldArray
    name="fields"
    render={arrayHelpers => (
      <div>
        {fields.map(field => (
          <div key={field.name}>
            <label>
              <input
                name="fields"
                type="checkbox"
                value={field.name}
                onChange={e => {
                  if (e.target.checked) arrayHelpers.push(field.name);
                  else {
                    const idx = fields.indexOf(field.name);
                    arrayHelpers.remove(idx);
                  }
                }}
              />{" "}
              {field.name}
            </label>
          </div>
        ))}
      </div>
    )}
  />

因此,在onChange方法中,我需要选中每个复选框以呈现一个类组件,该类组件具有与field名称相关的其他输入字段。例如,需要为选中的每个复选框选择大小和长度选项。

        class FieldInputs extends React.Component {
          constructor(props) {
            super(props);

            this.state = {
              lengthType: "",
              size: [],
            };

            this.lengthTypeChange = this.lengthTypeChange.bind(this);
            this.onSizeChange = this.onSizeChange.bind(this);
          }

      lengthTypeChange = lengthType => {
        //handle change method for lengthType
        this.setState({ lengthType });
        console.log("LengthType selected: ", lengthType);
      };

  onSizeChange = e => {
    this.setState({ [e.target.name]: e.target.value });
    console.log([e.target.value]);
  };
          render() {
            return (
              <div>
                <h2>
                  {" "}
                  These are the input fields for each field name checkbox selected.{" "}
                </h2>
            <div>
              <Select
                id="color"
                options={lengthTypeOptions}
                isMulti={false}
                value={lengthType}
                onChange={this.lengthTypeChange}
                onBlur={this.handleBlur}
                placeholder={"Select a lengthType..."}
              />
            </div>
            <div>
              <label>Size:</label>
              <input
                value={this.state.size}
                onChange={this.onSizeChange}
                type="number"
                name="size"
                min="1"
                placeholder="1"
                required
              />
            </div>
              </div>
            );
          }
        }

例如,每个field.name都呈现到一个复选框,并且应该具有lengthType的选择下拉列表和size的输入,如下所示:

{
    field.name: {
      size: 1,
      lengthType: "Fixed"
    }
}

所以我已经设计了组件,只需要将其渲染到选中的每个复选框即可。 如何根据是否切换FieldInput组件将其传递给复选框?

<Myselect>组件内,您可以使用<FieldArray>方法

找到onChange组件
onChange={e => {
  if (e.target.checked) {
    arrayHelpers.push(field.name);

在选中时还需要渲染<FieldInputs>

这里是link to a sandbox,其中包含上述类/组件

1 个答案:

答案 0 :(得分:1)

我做了很多更改,这是完整的代码

import "./helper.css";
import { MoreResources, DisplayFormikState } from "./helper";

import React from "react";
import { render } from "react-dom";
import { Formik, FieldArray } from "formik";
import * as Yup from "yup";
import axios from "axios";
import Select from "react-select";

var MockAdapter = require("axios-mock-adapter");
var mock = new MockAdapter(axios);

mock.onGet("/dataschemas").reply(200, {
  data: [
    {
      id: "2147483602",
      selfUri: "/dataschemas/2147483602",
      name: "Phone Data"
    }
  ]
});

mock.onGet("/dataschemas/2147483602").reply(200, {
  data: {
    id: "2147483602",
    selfUri: "/dataschemas/2147483602",
    type: "DataSchema",
    name: "Phone Record",
    fields: [
      {
        name: "action"
      },
      {
        name: "callee"
      },
      {
        name: "caller"
      },
      {
        name: "duration"
      },
      {
        name: "message"
      },
      {
        name: "time_stamp"
      }
    ]
  }
});

const lengthTypeOptions = [
  { value: "fixed", label: "Fixed" },
  { value: "variable", label: "Variable" }
];

class FieldInputs extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      lengthType: "",
      size: []
    };

    this.lengthTypeChange = this.lengthTypeChange.bind(this);
    this.onSizeChange = this.onSizeChange.bind(this);
  }

  lengthTypeChange = lengthType => {
    //handle change method for lengthType
    this.setState({ lengthType }, () => {
      this.props.update(this.props.name, this.state);
    });
    //console.log("LengthType selected: ", lengthType);
  };

  onSizeChange = e => {
    this.setState({ [e.target.name]: e.target.value }, () => {
      this.props.update(this.props.name, this.state);
    });
    //console.log([e.target.value]);
  };

  render() {
    const { lengthType } = this.state;
    return (
      <div>
        <h2>
          {" "}
          These are the input fields for each field name checkbox selected.{" "}
        </h2>
        <div>
          <Select
            id="color"
            options={lengthTypeOptions}
            isMulti={false}
            value={lengthType}
            onChange={this.lengthTypeChange}
            onBlur={this.handleBlur}
            placeholder={"Select a lengthType..."}
          />
        </div>
        <div>
          <label>Size:</label>
          <input
            value={this.state.size}
            onChange={this.onSizeChange}
            type="number"
            name="size"
            min="1"
            placeholder="1"
            required
          />
        </div>
      </div>
    );
  }
}

const App = () => (
  <div className="app">
    <h1>Formik Demo</h1>

    <Formik
      initialValues={{
        querySchemaName: "",
        schemas: [],
        fields: [],
        selectorField: "",
        lengthType: "",
        size: []
      }}
      onSubmit={(values, { setSubmitting }) => {
        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
          setSubmitting(false);
        }, 500);
      }}
      validationSchema={Yup.object().shape({
        querySchemaName: Yup.string()
          .required("QuerySchema name is required!")
          .min(3, "Please enter a longer name")
          .max(50, "Please ener a shorter name")
      })}
    >
      {props => {
        const {
          values,
          touched,
          errors,
          dirty,
          isSubmitting,
          handleChange,
          handleBlur,
          handleSubmit,
          handleReset,
          setFieldValue,
          setTouchedValue
        } = props;
        return (
          <form onSubmit={handleSubmit}>
            <label htmlFor="querySchemaName" style={{ display: "block" }}>
              QuerySchema Name:
            </label>
            <input
              id="querySchemaName"
              placeholder="Example -- QuerySchema1"
              type="text"
              value={values.querySchemaName}
              onChange={handleChange}
              onBlur={handleBlur}
              className={
                errors.querySchemaName && touched.querySchemaName
                  ? "text-input error"
                  : "text-input"
              }
            />
            {errors.querySchemaName && touched.emaquerySchemaNameil && (
              <div className="input-feedback">{errors.querySchemaName}</div>
            )}
            <MySelect
              value={values.schemas}
              onChange={setFieldValue}
              options={values.schemas}
              onBlur={setTouchedValue}
              error={errors.topics}
              touched={touched.topics}
            />

            <button
              type="button"
              className="outline"
              onClick={handleReset}
              disabled={!dirty || isSubmitting}
            >
              Reset
            </button>
            <button type="submit" disabled={isSubmitting}>
              Submit
            </button>

            <DisplayFormikState {...props} />
          </form>
        );
      }}
    </Formik>

    <MoreResources />
  </div>
);

class MySelect extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      schemas: [],
      fields: [],
      selectorField: "",
      checked: []
    };

    this.handleChange = this.handleChange.bind(this);
    this.updateSelectorField = this.updateSelectorField.bind(this);
  }

  componentDidMount() {
    axios.get("/dataschemas").then(response => {
      this.setState({
        schemas: response.data.data
      });
      //console.log(this.state.schemas);
    });
  }

  handleChange = value => {
    // this is going to call setFieldValue and manually update values.dataSchemas
    this.props.onChange("schemas", value);
    const schema = this.state.schemas.find(
      schema => schema.name === value.name
    );
    if (schema) {
      axios.get("/dataschemas/2147483602").then(response => {
        this.setState({
          fields: response.data.data.fields
        });
        //console.log("fields are: " + this.state.fields);
      });
    }
  };

  updateSelectorField = value => {
    this.props.onChange("selectorField", value);
  };

  update = (name, value) => {
    this.setState(prev => {
      var arr = prev.checked;
      var de = null;
      for (var j = 0; j < arr.length; j++) {
        if (arr[j].name === name) {
          de = j;
        }
      }
      arr[de] = Object.assign(arr[de], value);
      console.log(arr);
      return { checked: arr };
    });
  };

  handleBlur = () => {
    // this is going to call setFieldTouched and manually update touched.dataSchemas
    this.props.onBlur("schemas", true);
  };

  checker = field => {
    var d = -1;
    for (let j = 0; j < this.state.checked.length; j++) {
      if (this.state.checked[j].name === field.name) {
        d = j;
      }
    }

    if (d >= 0) {
      return (
        <FieldInputs
          key={field.name + 1}
          name={field.name}
          update={this.update}
        />
      );
    } else {
      return null;
    }
  };

  change = e =>{
    var arr = this.state.checked;
    var de = -1;
    for (var j = 0; j < arr.length; j++) {
      if (arr[j].name === e) {
        de = j;
      }
    }
    if(de >= 0){
      delete arr[de];
    }else{
      var arr = arr.concat([
        {
          name: e,
          size: 1,
          lengthType: "Fixed"
        }
      ])
    }
    var nar = [];
    for(let i=0; i<arr.length; i++){
      if(typeof arr[i] !== "undefined"){
        nar.push(arr[i])
      }
    }
    this.setState({checked: nar});
  }

  render() {
    const schemas = this.state.schemas;
    const fields = this.state.fields;
    return (
      <div style={{ margin: "1rem 0" }}>
        <label htmlFor="color">
          DataSchemas -- triggers the handle change api call - (select 1){" "}
        </label>
        <Select
          id="color"
          options={schemas}
          isMulti={false}
          value={schemas.find(({ name }) => name === this.state.name)}
          getOptionLabel={({ name }) => name}
          onChange={this.handleChange}
          onBlur={this.handleBlur}
          placeholder={"Pick a DataSchema..."}
        />
        <label htmlFor="color">Selector Field - (select 1) </label>
        <Select
          id="color"
          options={fields}
          isMulti={false}
          value={fields.find(({ name }) => name === this.state.name)}
          getOptionLabel={({ name }) => name}
          onChange={this.updateSelectorField}
          placeholder={"Select a Selector Field..."}
        />
        {!!this.props.error && this.props.touched && (
          <div style={{ color: "red", marginTop: ".5rem" }}>
            {this.props.error}
          </div>
        )}
        <div>
          <FieldArray
            name="fields"
            render={arrayHelpers => (
              <div>
                {fields.map(field => (
                  <React.Fragment>
                    <div key={field.name}>
                      <label>
                        <input
                          name="fields"
                          type="checkbox"
                          value={field.name}
                          onChange={(e) => {
                            this.change(field.name)
                            if (e.target.checked) {
                              arrayHelpers.push(field.name);
                            } else {
                              const idx = fields.indexOf(field.name);
                              arrayHelpers.remove(idx);
                            }
                            }}
                        />{" "}
                        {field.name}
                      </label>
                    </div>
                    {this.checker(field)}
                  </React.Fragment>
                ))}
              </div>
            )}
          />
        </div>
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

这是codesandbox