formik 嵌套动态对象

formik nested dynamic object

能否请您针对以下问题提出解决方案:

例如我们有一个包含许多交付选项的结帐表格,每个选项都需要不同的详细信息,例如:

状态示例:

...,
delivery: {
  type: 'inStore',
  details: { storeId: 1 }
}

...,
delivery: {
  type: 'courier',
  details: {
    address: {
      street: '12 Ave',
      ...,
     }
   }
 }

一旦只能检查上述选项之一,我就尝试使用单选按钮组件来定义 delivery type 此外,还可以根据 delivery type 呈现不同的组件(例如通过 switch-case)并填充不同的 delivery.___ 字段。

但是如果我们填写 inStore 选项的表格然后切换到例如courier选项storeId属性仍处于状态。那么如何正确处理这个开关(根据所选 delivery type

只剩下必需的属性

在你的案例中,React 尝试为 storecourier 的第一个 Fieldcourier 案例重新使用相同的输入组件,因此它不必呈现一个全新的节点。您可以通过向每个 Field 组件添加一个独特的 key 道具来轻松解决此问题:

  switch (deliveryType) {
    case "store":
      return (
        <Field
          key={`${name}.storeId`}
          name={`${name}.storeId`}
          value={1}
          placeholder="storeId"
        />
      );
    case "courier":
      return (
        <React.Fragment>
          <Field
            key={`${name}.address.street`}
            name={`${name}.address.street`}
            placeholder="street"
          />
          <Field
            key={`${name}.address.building`}
            name={`${name}.address.building`}
            placeholder="building"
          />
          <Field
            key={`${name}.address.flat`}
            name={`${name}.address.flat`}
            placeholder="flat"
          />
        </React.Fragment>
      );
    case "delivery_office":
      return (
        <React.Fragment>
          <Field
            key={`${name}.cityId`}
            name={`${name}.cityId`}
            placeholder="cityId"
          />
          <Field
            key={`${name}.officeId`}
            name={`${name}.officeId`}
            placeholder="officeId"
          />
        </React.Fragment>
      );
    default:
      return null;
  }

至于现在,我已将子表单状态重置添加到单选按钮 onChange 事件处理程序。因此基于新的单选按钮值(例如deliveryType)计算出新的初始状态(例如deliveryDetails)。

正因为如此,新的 deliveryType 和新的 initialState 允许渲染相关的子表单没有任何问题。

请在此处检查解决方案:https://codesandbox.io/s/formik-example-forked-ix0nt?file=/deliveryDetailsForm.js

值得一提的是,需要先更新子表单的初始状态,再更新因变量(如deliveryType)。因此,一旦 values.delivery.details 对象在 之前更新所需的子表单,就不会发生 'controlled-to-uncontrolled' 错误。

P.S。当前的解决方案使用 switch-case 来决定应该呈现哪个 initialState 和哪个子表单,这是一个非常糟糕的设计,所以如果您有任何关于如何让它变得更好的建议,我会很高兴听到他们。