如何根据 ReactJS 中的选择更改表单初始值和字段?

How to change Form initialValues and Fields based on selection in ReactJS?

我有一个这样的对象:

const initialValues = {
    "Name": "",
    "Price": "",
    "Quantity": "",
    "Details": {
        "Light": "",
        "Watering": "",
        "Maintenance": "",
        "WhereToGrow": "",
    },
    "BrowseImg": "",
    "MainImg": "",
    "Tags": "",
    "Category": "Plant",
    "SubCategory": ""
}

我在我的表单组件中使用 React Formik 库,我正在渲染 selection& Details object 像这样:


  <Grid item xs={6}>
    <FormControl required>
      <InputLabel id="demo-simple-select-required-label">Category</InputLabel>
      <Select
        labelId="demo-simple-select-required-label"
        id="demo-simple-select-required"
        value={values.Category !== "undefined" ? values.Category : ""}
        label="Category *"
        onChange={(e) => {
          setFieldValue("Category", e.target.value);
        }}
        autoWidth
        name="Category"
        defaultValue=""
      >
        <MenuItem value="">
          <em>None</em>
        </MenuItem>
        <MenuItem value={"Plant"}>Plant</MenuItem>
        <MenuItem value={"Planters"}>Planters</MenuItem>
        <MenuItem value={"Seeds"}>Seeds</MenuItem>
      </Select>
    </FormControl>
  </Grid>
  <FieldArray name="Details">
    <Grid container spacing={2} sx={{ padding: "20px" }}>
      <Grid item xs={12}>
        <Typography>Details</Typography>
      </Grid>

      <Grid item xs={6}>
        <TextFieldWrapper
          name={
            values.Category !== "Planters"
              ? `Details.Light`
              : `Details.Material`
          }
          label={values.Category !== "Planters" ? `Light` : "Material"}
        />
      </Grid>
      <Grid item xs={6}>
        <TextFieldWrapper
          name={
            values.Category !== "Planters"
              ? `Details.Watering`
              : `Details.Build`
          }
          label={values.Category !== "Planters" ? `Watering` : "Build"}
        />
      </Grid>
      <Grid item xs={6}>
        <TextFieldWrapper
          name={
            values.Category !== "Planters"
              ? `Details.Maintenance`
              : `Details.PlanterHeight`
          }
          label={
            values.Category !== "Planters" ? `Maintenance` : "Planter Height"
          }
        />
      </Grid>
      <Grid item xs={6}>
        <TextFieldWrapper
          name={
            values.Category !== "Planters"
              ? `Details.WhereToGrow`
              : `Details.PlanterWidth`
          }
          label={
            values.Category !== "Planters" ? `Where To Grow` : "Planter Width"
          }
        />
      </Grid>
    </Grid>
  </FieldArray>

现在,我需要将选择 "Planters" 时的 initialValues 对象更改为:

{
    "Name": "",
    "Price": "",
    "Quantity": "",
    "Details": {
        "Material": "",
        "Build": "",
        "PlanterHeight": "",
        "PlanterWidth": ""
    },
    "BrowseImg": "",
    "MainImg": "",
    "Tags": "",
    "Category": "Planters",
    "SubCategory": ""
}

我试过这样更改 initialValues 对象中的键名:

    const Context = () => {
      const { values } = useFormikContext();

      useEffect(() => {
        if (values.Category === "Planters") {
          delete Object.assign(initialValues.Details, {
            Material: initialValues.Details.Light,
          })["Light"];
          delete Object.assign(initialValues.Details, {
            Build: initialValues.Details.Watering,
          })["Watering"];
          delete Object.assign(initialValues.Details, {
            PlanterHeight: initialValues.Details.Maintenance,
          })["Maintenance"];
          delete Object.assign(initialValues.Details, {
            PlanterWidth: initialValues.Details.WhereToGrow,
          })["WhereToGrow"];

          console.log("VALUES FORMIK CONTEXT", values);
          console.log("INITIALVALUES", initialValues);
          console.log(
            "INITIAL VALUES DETAILS ",
            initialValues.Details.Material
          );
        }
      }, [values]);
      return null;
    };

values.Category 是 Planters 时更改名称会抛出 Formik 错误,如下所示:

Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.

我该如何实现?提前致谢!

这不是很正确的方法,更改表单中的字段集对我来说不是个好主意 on-the-fly - 你在支持自己的混乱代码时会遇到问题,这个 useEffect真是一团糟。

请记住,您用于 UI 的表单数据可能与您发送到后端的数据不同,并且可以在您发出请求之前进行转换。实际上 initialValues 用于初始化您的表单,不应从表单内部更改。我会创建通用字段集。

例如,我会像这样更改数据初始数据

// read-only array to render Select, add as much item as you like.
// In general, since it's just an array for options,
// it can be safely extracted to external constant
// and may not present in initialData. 
Categories: [
 {
  name 'Planers',
  selected: true,
 },
 {
   name: 'Plants'
 } 
],
SelectedCategory: 'Planers', // since selected is true 
Planters: {
  "Material": "",
  "Build": "",
  "PlanterHeight": "",
  "PlanterWidth": ""
},
Plants: {
  "Light": "",
  "Watering": "",
  "Maintenance": "",
  "WhereToGrow": "",
}

然后在你的表单中

// ...
const { values, handleChange, handleBlur } = useFormikContext();

<Select
  onChange={handleChange} // or your own handler
  onBlur={handleBlur}
  name="SelectedCategory"
>
{values.Categories.map(({name, selected}) => (
  <MenuItem
   key={name}
   selected={selected}
  >
   {name}
  </MenuItem >
))}
</Select>

并且在同一组件中有两组或更多组字段。每组根据 SelectedCategory 值渲染。 SelectedCategory 更改时无需更改或重置字段集中的值 - 您可以在发送数据阶段删除或重置字段。

if (values.SelectedCategory === 'Planers') {
  // render set of fields for Planers
}
if (values.SelectedCategory === 'Plants') {
  // render set of fields for Plants
}

并且 onSubmit 您可以根据需要转换您的字段值:

<Formik
  initialValues
  onSubmit={(values, {resetForm}) => {
      const transformedDataForAsyncRequest = yourCustomDataTransducer(values);
      axios({
        method: 'post',
        data: transformedDataForAsyncRequest
      })
        .then(() => {resetForm()})
        .catch((e) => {console.log(e)})
    }
  }