无法使用Formik在FieldArray中设置setFieldValue

时间:2020-09-16 10:58:45

标签: reactjs typescript forms formik

感谢阅读。

我正在尝试使用Formik构建表单。并且它在FieldArray中包含一个FieldArray。

由于某种原因,setFieldValue可以正常运行,可以用console.log记录正确的e.target.namee.target.value

问题在于提交表单时,来自输入的所有值均未位于应有的位置。预期的行为是,值应位于exclusionGroups内部,而不是外部。

任何人都可以给我一些见识吗?我整天都被困在这里,感觉好像我的头会爆炸。

预期:

exclusionGroups: [
  {
    exclusion: [
      {
        param: 'delayMin',
        operator: '<',
        value: 'test1',
      },
      {
        param: 'airlineCode',
        operator: '>',
        value: 'test2',
      },
    ],
  },
  {
    exclusion: [
      {
        param: 'flightNumber',
        operator: '=',
        value: 'test3',
      },
    ],
  },
],

现实:

exclusionGroups: (2) [{…}, {…}]
group-1-operator-1: "<"
group-1-operator-2: ">"
group-1-param-1: "delayMin"
group-1-param-2: "airlineCode"
group-1-value-1: "test1"
group-1-value-2: "test2"
group-2-operator-1: "="
group-2-param-1: "flightNumber"
group-2-value-1: "test3"

我的代码:

Index.tsx

type ExclusionRuleValues = {
  param: string;
  operator: string;
  value: string;
};

interface ExclusionGroupProps {
  exclusionRules?: ExclusionRuleValues[];
}

const Exclusion = ({ data, type }: any) => {
  const onSubmit = (values: any) => {
    console.log(values);
  };

  const initialValues = {
    exclusionGroups: [
      {
        exclusion: [
          {
            param: 'group1',
            operator: 'group1',
            value: 'group1',
          },
        ],
      },
      {
        exclusion: [
          {
            param: 'group2',
            operator: 'group2',
            value: 'group2',
          },
        ],
      },
    ],
  };

  const emptyGroup = {
    exclusion: [
      {
        param: '',
        operator: '',
        value: '',
      },
    ],
  };

  return (
    <React.Fragment>
      <Formik
        enableReinitialize
        onSubmit={onSubmit}
        initialValues={initialValues}
        render={(formProps) => {
          const { values, handleSubmit, submitForm } = formProps;

          const { exclusionGroups } = values;

          const setFieldValue = (e: ChangeEvent<HTMLInputElement>) => {
            return formProps.setFieldValue(e.target.name, e.target.value);
          };

          return (
            <React.Fragment>
              <Header>
                Model will return excluded message code if the following condition is true.
                <Button onClick={submitForm} color="primary">
                  Save Changes
                </Button>
              </Header>

              <Form onSubmit={handleSubmit}>
                <FieldArray
                  name="exclusionGroups"
                  render={(arrayHelper) => (
                    <React.Fragment>
                      {exclusionGroups.map((exclusionRulesGroup: any, index: number) => {
                        return (
                          <TargetFields
                            type={type}
                            group={index + 1}
                            key={`field-${index}`}
                            name={`${arrayHelper.name}.${index}`}
                            setFieldValue={setFieldValue}
                          />
                        );
                      })}
                      <AddNewGroupButton type="button" onClick={() => arrayHelper.push(emptyGroup)}>
                        + New Group
                      </AddNewGroupButton>
                    </React.Fragment>
                  )}
                />
              </Form>
            </React.Fragment>
          );
        }}
      />{' '}
    </React.Fragment>
  );
};

export default Exclusion;

TargetFields.tsx

interface TargetFieldsProps {
  group: number;
  name: string;
  type: string;
  data?: ExclusionRuleValues[];
  setFieldValue: (e: ChangeEvent<HTMLInputElement>) => void;
}

const TargetFields = ({ group, data, name, type, setFieldValue }: TargetFieldsProps) => {
  const emptyTarget = {
    param: '',
    operator: '',
    value: '',
  };

  return (
    <React.Fragment>
      <Field name={name}>
        {(fieldProps: any) => (
          <React.Fragment>
            <ExclusionGroupHeader>
              <b>Group {group}</b>
            </ExclusionGroupHeader>

            <Wrapper>
              <FieldArray
                name={`${fieldProps.field.name}.exclusion`}
                key={`exclusion-${group}`}
                render={(targetHelper) => (
                  <React.Fragment>
                    {fieldProps.field.value.exclusion.map(
                      (target: ExclusionRuleValues, key: number) => {
                        const { param, operator, value } = target;
                        return (
                          <ExclusionRuleGroup key={`group-${key}`}>
                            <ExclusionRuleHeader>
                              <b>Target {key + 1}</b>
                              <DeleteButton type="button" onClick={() => targetHelper.remove(key)}>
                                remove
                              </DeleteButton>
                            </ExclusionRuleHeader>
                            <StyledRow>
                              <CCol sm="4">
                                <Select
                                  onChange={setFieldValue}
                                  // value={param}
                                  label="Params"
                                  name={`group-${group}-param-${key + 1}`}
                                  options={
                                    type === 'input' ? InputExclusionParams : OutputExclusionParams
                                  }
                                  placeholder="Operator"
                                />
                              </CCol>
                              <CCol sm="4">
                                <Select
                                  onChange={setFieldValue}
                                  // value={operator}
                                  label="Operator"
                                  name={`group-${group}-operator-${key + 1}`}
                                  options={SelectOptions}
                                  placeholder="Operator"
                                />
                              </CCol>
                              <CCol sm="4">
                                <Input
                                  onChange={setFieldValue}
                                  // value={value}
                                  label="Value"
                                  name={`group-${group}-value-${key + 1}`}
                                  type="text"
                                  placeholder="Value"
                                />
                              </CCol>
                            </StyledRow>
                          </ExclusionRuleGroup>
                        );
                      }
                    )}
                    <AddNewRuleButton type="button" onClick={() => targetHelper.push(emptyTarget)}>
                      + New Rule
                    </AddNewRuleButton>
                  </React.Fragment>
                )}
              />
            </Wrapper>
          </React.Fragment>
        )}
      </Field>
    </React.Fragment>
  );
};

export default TargetFields;

1 个答案:

答案 0 :(得分:0)

问题是当您将name属性传递给表单的输入时。

在组件TargetFields中,您传递的名称类似name={`group-${group}-operator-${key + 1}`},因此formik认为您希望将该值存储在该字符串产生的属性中,例如group-1-operator-1,这就是为什么要使用您想要的对象,而要使用具有该名称的属性的原因。

如果要使用嵌套的对象/数组,则需要使用.[${index}]将名称与想要的对象连接起来,就像您在此处所做的那样

<TargetFields
    type={type}
    group={index + 1}
    key={`field-${index}`}
    name={`${arrayHelper.name}.${index}`}  // You use the . with the name and the index. 
    setFieldValue={setFieldValue}          // It could also be using [] instead of . like this => `${arrayHelper.name}[${index}]`
/>

就像这里

<FieldArray
    name={`${fieldProps.field.name}.exclusion`} // You use the . with the name and the property of the object you want
    key={`exclusion-${group}`}
    render={(targetHelper) => ( ... )
/>

因此,要解决您的问题,您需要更改以下内容。

<StyledRow>
  <CCol sm="4">
    <Select
      onChange={setFieldValue}
      // value={param}
      label="Params"
      name={`${targetHelper}[${key}].param`}
      options={
        type === 'input' ? InputExclusionParams : OutputExclusionParams
      }
      placeholder="Operator"
    />
  </CCol>
  <CCol sm="4">
    <Select
      onChange={setFieldValue}
      // value={operator}
      label="Operator"
      name={`${targetHelper}[${key}].operator`}
      options={SelectOptions}
      placeholder="Operator"
    />
  </CCol>
  <CCol sm="4">
    <Input
      onChange={setFieldValue}
      // value={value}
      label="Value"
      name={`${targetHelper}[${key}].value`}
      type="text"
      placeholder="Value"
    />
  </CCol>
</StyledRow>

只是一个猜测,您不是凭自己的能力编写了所有代码?因为在一个地方,您正在做解决您的问题所需的一切。如果您不知道该名称如何与FieldArray一起使用,建议您阅读formik docs的这一部分。知道如何工作对于使用嵌套对象/数组非常重要。